From f345b76895b5c2a232d8412e3844e10871432f38 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 6 Jun 2025 16:09:45 +0800 Subject: [PATCH 001/201] Add generate_impl_trait for generate_impl --- .../ide-assists/src/handlers/generate_impl.rs | 272 +++++++++++++++++- .../crates/ide-assists/src/lib.rs | 1 + .../crates/ide-assists/src/tests/generated.rs | 23 ++ 3 files changed, 294 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 14601ca02076..ba95fe5d92a3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -1,12 +1,17 @@ use syntax::{ - ast::{self, AstNode, HasName, edit_in_place::Indent, make}, + ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make}, syntax_editor::{Position, SyntaxEditor}, }; -use crate::{AssistContext, AssistId, Assists, utils}; +use crate::{ + AssistContext, AssistId, Assists, + utils::{self, DefaultMethods, IgnoreAssocItems}, +}; fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) { let indent = nominal.indent_level(); + + impl_.indent(indent); editor.insert_all( Position::after(nominal.syntax()), vec![ @@ -120,6 +125,101 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } +// Assist: generate_impl_trait +// +// Adds this trait impl for a type. +// +// ``` +// trait $0Foo { +// fn foo(&self) -> i32; +// } +// ``` +// -> +// ``` +// trait Foo { +// fn foo(&self) -> i32; +// } +// +// impl Foo for ${1:_} { +// $0fn foo(&self) -> i32 { +// todo!() +// } +// } +// ``` +pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let name = ctx.find_node_at_offset::()?; + let trait_ = ast::Trait::cast(name.syntax().parent()?)?; + let target_scope = ctx.sema.scope(trait_.syntax())?; + let hir_trait = ctx.sema.to_def(&trait_)?; + + let target = trait_.syntax().text_range(); + acc.add( + AssistId::generate("generate_impl_trait"), + format!("Generate `{name}` impl for type"), + target, + |edit| { + let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder())); + let missing_items = utils::filter_assoc_items( + &ctx.sema, + &hir_trait.items(ctx.db()), + DefaultMethods::No, + IgnoreAssocItems::DocHiddenAttrPresent, + ); + let impl_ = make::impl_trait( + trait_.unsafe_token().is_some(), + None, + trait_.generic_param_list().map(|list| { + make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone())) + }), + None, + None, + false, + make::ty(&name.text()), + make::ty_placeholder(), + None, + None, + None, + ) + .clone_for_update(); + + let trait_ = edit.make_mut(trait_); + + if !missing_items.is_empty() { + utils::add_trait_assoc_items_to_impl( + &ctx.sema, + ctx.config, + &missing_items, + hir_trait, + &impl_, + &target_scope, + ); + } + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { + for generic in generics.generic_args() { + edit.add_placeholder_snippet(cap, generic); + } + } + + if let Some(ty) = impl_.self_ty() { + edit.add_placeholder_snippet(cap, ty); + } + + if let Some(item) = impl_.assoc_item_list().and_then(|it| it.assoc_items().next()) { + edit.add_tabstop_before(cap, item); + } else if let Some(l_curly) = + impl_.assoc_item_list().and_then(|it| it.l_curly_token()) + { + edit.add_tabstop_after_token(cap, l_curly); + } + } + + insert_impl(impl_, &trait_); + }, + ) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_target}; @@ -492,4 +592,172 @@ mod tests { "#, ); } + + #[test] + fn test_add_impl_trait() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + "#, + r#" + trait Foo { + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + + impl Foo for ${1:_} { + $0fn foo(&self) -> i32 { + todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_use_generic() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + "#, + r#" + trait Foo { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + + impl Foo<${1:_}> for ${2:_} { + $0fn foo(&self) -> _ { + todo!() + } + } + "#, + ); + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + "#, + r#" + trait Foo { + fn foo(&self) -> T; + + fn bar(&self) -> T { + self.foo() + } + } + + impl Foo<${1:_}, ${2:_}> for ${3:_} { + $0fn foo(&self) -> _ { + todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_docs() { + check_assist( + generate_impl_trait, + r#" + /// foo + trait $0Foo { + /// foo method + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + "#, + r#" + /// foo + trait Foo { + /// foo method + fn foo(&self) -> i32; + + fn bar(&self) -> i32 { + self.foo() + } + } + + impl Foo for ${1:_} { + $0fn foo(&self) -> i32 { + todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_assoc_types() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + "#, + r#" + trait Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + + impl Foo for ${1:_} { + $0type Output; + + fn foo(&self) -> Self::Output { + todo!() + } + } + "#, + ); + } + + #[test] + fn test_add_impl_trait_empty() { + check_assist( + generate_impl_trait, + r#" + trait $0Foo {} + "#, + r#" + trait Foo {} + + impl Foo for ${1:_} {$0} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index c2604432032d..46a4c85f41ad 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -301,6 +301,7 @@ mod handlers { generate_function::generate_function, generate_impl::generate_impl, generate_impl::generate_trait_impl, + generate_impl::generate_impl_trait, generate_is_empty_from_len::generate_is_empty_from_len, generate_mut_trait_impl::generate_mut_trait_impl, generate_new::generate_new, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index fe74694d8a44..7c88677be1a1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1880,6 +1880,29 @@ impl Ctx {$0} ) } +#[test] +fn doctest_generate_impl_trait() { + check_doc_test( + "generate_impl_trait", + r#####" +trait $0Foo { + fn foo(&self) -> i32; +} +"#####, + r#####" +trait Foo { + fn foo(&self) -> i32; +} + +impl Foo for ${1:_} { + $0fn foo(&self) -> i32 { + todo!() + } +} +"#####, + ) +} + #[test] fn doctest_generate_is_empty_from_len() { check_doc_test( From e0dddfc75321dd196b5f67da4eb7f22d1f276992 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sat, 28 Jun 2025 15:43:04 +0800 Subject: [PATCH 002/201] Change tabstop to method tail_expr --- .../ide-assists/src/handlers/generate_impl.rs | 37 ++++++++++++------- .../crates/ide-assists/src/tests/generated.rs | 4 +- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index ba95fe5d92a3..e72062bf9953 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -141,8 +141,8 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> // } // // impl Foo for ${1:_} { -// $0fn foo(&self) -> i32 { -// todo!() +// fn foo(&self) -> i32 { +// $0todo!() // } // } // ``` @@ -206,8 +206,10 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> edit.add_placeholder_snippet(cap, ty); } - if let Some(item) = impl_.assoc_item_list().and_then(|it| it.assoc_items().next()) { - edit.add_tabstop_before(cap, item); + if let Some(expr) = + impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr)) + { + edit.add_tabstop_before(cap, expr); } else if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) { @@ -220,6 +222,13 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) } +fn extract_expr(item: ast::AssocItem) -> Option { + let ast::AssocItem::Fn(f) = item else { + return None; + }; + f.body()?.tail_expr() +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_target}; @@ -616,8 +625,8 @@ mod tests { } impl Foo for ${1:_} { - $0fn foo(&self) -> i32 { - todo!() + fn foo(&self) -> i32 { + $0todo!() } } "#, @@ -647,8 +656,8 @@ mod tests { } impl Foo<${1:_}> for ${2:_} { - $0fn foo(&self) -> _ { - todo!() + fn foo(&self) -> _ { + $0todo!() } } "#, @@ -674,8 +683,8 @@ mod tests { } impl Foo<${1:_}, ${2:_}> for ${3:_} { - $0fn foo(&self) -> _ { - todo!() + fn foo(&self) -> _ { + $0todo!() } } "#, @@ -709,8 +718,8 @@ mod tests { } impl Foo for ${1:_} { - $0fn foo(&self) -> i32 { - todo!() + fn foo(&self) -> i32 { + $0todo!() } } "#, @@ -736,10 +745,10 @@ mod tests { } impl Foo for ${1:_} { - $0type Output; + type Output; fn foo(&self) -> Self::Output { - todo!() + $0todo!() } } "#, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index 7c88677be1a1..b63571ca6a12 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -1895,8 +1895,8 @@ trait Foo { } impl Foo for ${1:_} { - $0fn foo(&self) -> i32 { - todo!() + fn foo(&self) -> i32 { + $0todo!() } } "#####, From 71269047bee4c8678fa9aec34f0b3ad765fc8033 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Fri, 18 Jul 2025 06:39:51 +0800 Subject: [PATCH 003/201] Use SyntaxEditor --- .../ide-assists/src/handlers/generate_impl.rs | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index e72062bf9953..168eba22b3ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -8,7 +8,7 @@ use crate::{ utils::{self, DefaultMethods, IgnoreAssocItems}, }; -fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) { +fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) { let indent = nominal.indent_level(); impl_.indent(indent); @@ -158,6 +158,8 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> format!("Generate `{name}` impl for type"), target, |edit| { + let mut editor = edit.make_editor(trait_.syntax()); + let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder())); let missing_items = utils::filter_assoc_items( &ctx.sema, @@ -182,8 +184,6 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> ) .clone_for_update(); - let trait_ = edit.make_mut(trait_); - if !missing_items.is_empty() { utils::add_trait_assoc_items_to_impl( &ctx.sema, @@ -198,26 +198,31 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> if let Some(cap) = ctx.config.snippet_cap { if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { for generic in generics.generic_args() { - edit.add_placeholder_snippet(cap, generic); + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(generic.syntax(), placeholder); } } if let Some(ty) = impl_.self_ty() { - edit.add_placeholder_snippet(cap, ty); + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(ty.syntax(), placeholder); } if let Some(expr) = impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr)) { - edit.add_tabstop_before(cap, expr); + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(expr.syntax(), tabstop); } else if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) { - edit.add_tabstop_after_token(cap, l_curly); + let tabstop = edit.make_tabstop_after(cap); + editor.add_annotation(l_curly, tabstop); } } - insert_impl(impl_, &trait_); + insert_impl(&mut editor, &impl_, &trait_); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -755,6 +760,43 @@ mod tests { ); } + #[test] + fn test_add_impl_trait_indent() { + check_assist( + generate_impl_trait, + r#" + mod foo { + mod bar { + trait $0Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + } + } + "#, + r#" + mod foo { + mod bar { + trait Foo { + type Output; + + fn foo(&self) -> Self::Output; + } + + impl Foo for ${1:_} { + type Output; + + fn foo(&self) -> Self::Output { + $0todo!() + } + } + } + } + "#, + ); + } + #[test] fn test_add_impl_trait_empty() { check_assist( From 8acdee7bab65254f7cfc8f5e03ad180e65a45e9b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 13:16:42 +0200 Subject: [PATCH 004/201] don't halt execution when we write to a read-only file --- src/tools/miri/src/shims/files.rs | 11 ++++++++++- src/tools/miri/tests/pass/shims/fs.rs | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index 606d1ffbea6c..f7419b6bca29 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -1,7 +1,7 @@ use std::any::Any; use std::collections::BTreeMap; use std::fs::{File, Metadata}; -use std::io::{IsTerminal, Seek, SeekFrom, Write}; +use std::io::{ErrorKind, IsTerminal, Seek, SeekFrom, Write}; use std::marker::CoercePointee; use std::ops::Deref; use std::rc::{Rc, Weak}; @@ -334,6 +334,15 @@ impl FileDescription for FileHandle { ) -> InterpResult<'tcx> { assert!(communicate_allowed, "isolation should have prevented even opening a file"); + if !self.writable { + // Linux hosts return EBADF here which we can't translate via the platform-independent + // code since it does not map to any `io::ErrorKind` -- so if we don't do anything + // special, we'd throw an "unsupported error code" here. Windows returns something that + // gets translated to `PermissionDenied`. That seems like a good value so let's just use + // this everywhere, even if it means behavior on Unix targets does not match the real + // thing. + return finish.call(ecx, Err(ErrorKind::PermissionDenied.into())); + } let result = ecx.write_to_host(&self.file, len, ptr)?; finish.call(ecx, result) } diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 9d5725773e64..5e54cc382381 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -66,6 +66,10 @@ fn test_file() { assert!(!file.is_terminal()); + // Writing to a file opened for reading should error (and not stop interpretation). std does not + // categorize the error so we don't check for details. + file.write(&[]).unwrap_err(); + // Removing file should succeed. remove_file(&path).unwrap(); } From fe420251223e50b17577aad1e146ee66ec653a15 Mon Sep 17 00:00:00 2001 From: usamoi Date: Thu, 17 Jul 2025 20:50:49 +0800 Subject: [PATCH 005/201] update `Atomic*::from_ptr` and `Atomic*::as_ptr` docs --- library/core/src/sync/atomic.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 04c8d1473b04..c010b4d5a0b7 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -563,8 +563,8 @@ impl AtomicBool { /// `align_of::() == 1`). /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not - /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, - /// without synchronization. + /// allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different + /// sizes, without synchronization. /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses @@ -1246,7 +1246,7 @@ impl AtomicBool { /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction: operations on it must be atomic. + /// restriction in [Memory model for atomic accesses]. /// /// # Examples /// @@ -1264,6 +1264,8 @@ impl AtomicBool { /// } /// # } /// ``` + /// + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] @@ -1519,8 +1521,8 @@ impl AtomicPtr { /// can be bigger than `align_of::<*mut T>()`). /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not - /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, - /// without synchronization. + /// allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different + /// sizes, without synchronization. /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses @@ -2488,7 +2490,7 @@ impl AtomicPtr { /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction: operations on it must be atomic. + /// restriction in [Memory model for atomic accesses]. /// /// # Examples /// @@ -2507,6 +2509,8 @@ impl AtomicPtr { /// my_atomic_op(atomic.as_ptr()); /// } /// ``` + /// + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] @@ -2696,8 +2700,8 @@ macro_rules! atomic_int { }] /// * `ptr` must be [valid] for both reads and writes for the whole lifetime `'a`. /// * You must adhere to the [Memory model for atomic accesses]. In particular, it is not - /// allowed to mix atomic and non-atomic accesses, or atomic accesses of different sizes, - /// without synchronization. + /// allowed to mix conflicting atomic and non-atomic accesses, or atomic accesses of different + /// sizes, without synchronization. /// /// [valid]: crate::ptr#safety /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses @@ -3618,7 +3622,7 @@ macro_rules! atomic_int { /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction: operations on it must be atomic. + /// restriction in [Memory model for atomic accesses]. /// /// # Examples /// @@ -3638,6 +3642,8 @@ macro_rules! atomic_int { /// } /// # } /// ``` + /// + /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] From d3fc02caf5798f3af0e5294aee804496a480aa3e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 13:16:23 +0200 Subject: [PATCH 006/201] non-deterministically truncate reads/writes --- src/tools/miri/src/shims/files.rs | 5 ++ src/tools/miri/src/shims/unix/fd.rs | 21 ++++- .../miri/src/shims/unix/linux_like/eventfd.rs | 5 ++ src/tools/miri/src/shims/windows/fs.rs | 22 ++--- .../fail-dep/libc/libc-epoll-data-race.rs | 7 +- .../libc-read-and-uninit-premature-eof.rs | 8 +- .../libc/libc_epoll_block_two_thread.rs | 3 +- .../libc/socketpair_block_read_twice.rs | 25 +++--- .../libc/socketpair_block_read_twice.stderr | 4 +- .../libc/socketpair_block_write_twice.rs | 29 ++++--- .../libc/socketpair_block_write_twice.stderr | 4 +- .../pass-dep/libc/libc-epoll-blocking.rs | 7 +- .../pass-dep/libc/libc-epoll-no-blocking.rs | 44 +++++----- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 49 +++++++---- .../miri/tests/pass-dep/libc/libc-pipe.rs | 42 ++++++---- .../tests/pass-dep/libc/libc-socketpair.rs | 81 ++++++++++++------- src/tools/miri/tests/pass/shims/fs.rs | 28 ++++++- src/tools/miri/tests/pass/shims/pipe.rs | 4 +- src/tools/miri/tests/utils/fs.rs | 2 +- src/tools/miri/tests/utils/libc.rs | 44 ++++++++++ 20 files changed, 312 insertions(+), 122 deletions(-) create mode 100644 src/tools/miri/tests/utils/libc.rs diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs index f7419b6bca29..0d4642c6ad0e 100644 --- a/src/tools/miri/src/shims/files.rs +++ b/src/tools/miri/src/shims/files.rs @@ -167,6 +167,11 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt { throw_unsup_format!("cannot write to {}", self.name()); } + /// Determines whether this FD non-deterministically has its reads and writes shortened. + fn nondet_short_accesses(&self) -> bool { + true + } + /// Seeks to the given offset (which can be relative to the beginning, end, or current position). /// Returns the new position from the start of the stream. fn seek<'tcx>( diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 71102d9f2f33..b420955501c8 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -4,6 +4,7 @@ use std::io; use std::io::ErrorKind; +use rand::Rng; use rustc_abi::Size; use crate::helpers::check_min_vararg_count; @@ -263,9 +264,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + // Non-deterministically decide to further reduce the count, simulating a partial read (but + // never to 0, that has different behavior). + let count = + if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { + count / 2 + } else { + count + }; + trace!("read: FD mapped to {fd:?}"); // We want to read at most `count` bytes. We are sure that `count` is not negative - // because it was a target's `usize`. Also we are sure that its smaller than + // because it was a target's `usize`. Also we are sure that it's smaller than // `usize::MAX` because it is bounded by the host's `isize`. let finish = { @@ -328,6 +338,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + // Non-deterministically decide to further reduce the count, simulating a partial write (but + // never to 0, that has different behavior). + let count = + if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() { + count / 2 + } else { + count + }; + let finish = { let dest = dest.clone(); callback!( diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index ee7deb8d3830..2d35ef064db8 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -37,6 +37,11 @@ impl FileDescription for EventFd { "event" } + fn nondet_short_accesses(&self) -> bool { + // We always read and write exactly one `u64`. + false + } + fn close<'tcx>( self, _communicate_allowed: bool, diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index 72e016c12e94..e4ec1b0130c9 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -462,6 +462,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; + // It seems like short writes are not a thing on Windows, so we don't truncate `count` here. + // FIXME: if we are on a Unix host, short host writes are still visible to the program! + let finish = { let io_status = io_status.clone(); let io_status_info = io_status_info.clone(); @@ -491,7 +494,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }} ) }; - desc.write(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; // Return status is written to `dest` and `io_status_block` on callback completion. @@ -556,6 +558,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let io_status_info = this.project_field_named(&io_status_block, "Information")?; + let fd = match handle { + Handle::File(fd) => fd, + _ => this.invalid_handle("NtWriteFile")?, + }; + + let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; + + // It seems like short reads are not a thing on Windows, so we don't truncate `count` here. + // FIXME: if we are on a Unix host, short host reads are still visible to the program! + let finish = { let io_status = io_status.clone(); let io_status_info = io_status_info.clone(); @@ -585,14 +597,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }} ) }; - - let fd = match handle { - Handle::File(fd) => fd, - _ => this.invalid_handle("NtWriteFile")?, - }; - - let Some(desc) = this.machine.fds.get(fd) else { this.invalid_handle("NtReadFile")? }; - desc.read(this.machine.communicate(), buf, count.try_into().unwrap(), this, finish)?; // See NtWriteFile for commentary on this diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs index 314ce90cfb5d..f6ec5be61bb6 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs @@ -10,6 +10,9 @@ use std::convert::TryInto; use std::thread; use std::thread::spawn; +#[path = "../../utils/libc.rs"] +mod libc_utils; + #[track_caller] fn check_epoll_wait(epfd: i32, expected_notifications: &[(u32, u64)]) { let epoll_event = libc::epoll_event { events: 0, u64: 0 }; @@ -69,12 +72,12 @@ fn main() { unsafe { VAL_ONE = 41 }; let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds_a[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds_a[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); unsafe { VAL_TWO = 51 }; - let res = unsafe { libc::write(fds_b[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds_b[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); thread::yield_now(); diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs index 1dc334486c3a..e2fd6463a116 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs @@ -10,6 +10,9 @@ use std::mem::MaybeUninit; #[path = "../../utils/mod.rs"] mod utils; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { let path = utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]); @@ -18,8 +21,9 @@ fn main() { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit(); - // Read 4 bytes from a 3-byte file. - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 4), 3); + // Read as much as we can from a 3-byte file. + let res = libc_utils::read_all(fd, buf.as_mut_ptr().cast::(), 4); + assert!(res == 3); buf.assume_init(); //~ERROR: encountered uninitialized memory, but expected an integer assert_eq!(libc::close(fd), 0); } diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs index f6f2e2b93121..054cb812d9e8 100644 --- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs +++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs @@ -75,9 +75,10 @@ fn main() { }); let thread3 = spawn(move || { + // Just a single write, so we only wake up one of them. let data = "abcde".as_bytes().as_ptr(); let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; - assert_eq!(res, 5); + assert!(res > 0 && res <= 5); }); thread1.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs index b38398595002..0fecfb8f663f 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.rs @@ -4,6 +4,7 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock +//@require-annotations-for-level: error use std::thread; @@ -22,24 +23,26 @@ fn main() { assert_eq!(res, 0); let thread1 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; + let mut buf: [u8; 1] = [0; 1]; let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(&buf, "a".as_bytes()); }); let thread2 = thread::spawn(move || { // Let this thread block on read. - let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - //~^ERROR: deadlocked - assert_eq!(res, 3); - assert_eq!(&buf, "abc".as_bytes()); + let mut buf: [u8; 1] = [0; 1]; + let res = unsafe { + libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + //~^ERROR: deadlock + }; + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(&buf, "a".as_bytes()); }); let thread3 = thread::spawn(move || { // Unblock thread1 by writing something. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let data = "a".as_bytes(); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + assert_eq!(res, data.len().cast_signed()); }); thread1.join().unwrap(); thread2.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr index 9f19a60e6ae2..99d242ec7daf 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr @@ -23,8 +23,8 @@ error: the evaluated program deadlocked error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC | -LL | let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - | ^ this thread got stuck here +LL | libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + | ^ this thread got stuck here | = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at tests/fail-dep/libc/socketpair_block_read_twice.rs:LL:CC diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs index 7d84d87ebbb1..048938c091e3 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.rs @@ -4,16 +4,20 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency //@error-in-other-file: deadlock +//@require-annotations-for-level: error use std::thread; +#[path = "../../utils/libc.rs"] +mod libc_utils; + // Test the behaviour of a thread being blocked on write, get unblocked, then blocked again. // The expected execution is // 1. Thread 1 blocks. // 2. Thread 2 blocks. // 3. Thread 3 unblocks both thread 1 and thread 2. -// 4. Thread 1 reads. +// 4. Thread 1 writes. // 5. Thread 2's `write` can never complete -> deadlocked. fn main() { let mut fds = [-1, -1]; @@ -21,27 +25,28 @@ fn main() { assert_eq!(res, 0); let arr1: [u8; 212992] = [1; 212992]; // Exhaust the space in the buffer so the subsequent write will block. - let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let res = + unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; assert_eq!(res, 212992); let thread1 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); + let data = "a".as_bytes(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - assert_eq!(res, 3); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + assert_eq!(res, data.len().cast_signed()); }); let thread2 = thread::spawn(move || { - let data = "abc".as_bytes().as_ptr(); + let data = "a".as_bytes(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - //~^ERROR: deadlocked - assert_eq!(res, 3); + let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + //~^ERROR: deadlock + assert_eq!(res, data.len().cast_signed()); }); let thread3 = thread::spawn(move || { // Unblock thread1 by freeing up some space. - let mut buf: [u8; 3] = [0; 3]; + let mut buf: [u8; 1] = [0; 1]; let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(buf, [1, 1, 1]); + assert_eq!(res, buf.len().cast_signed()); + assert_eq!(buf, [1]); }); thread1.join().unwrap(); thread2.join().unwrap(); diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr index b29cd70f35e4..f766500d331d 100644 --- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr +++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr @@ -23,8 +23,8 @@ error: the evaluated program deadlocked error: the evaluated program deadlocked --> tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC | -LL | let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; - | ^ this thread got stuck here +LL | let res = unsafe { libc::write(fds[0], data.as_ptr() as *const libc::c_void, data.len()) }; + | ^ this thread got stuck here | = note: BACKTRACE on thread `unnamed-ID`: = note: inside closure at tests/fail-dep/libc/socketpair_block_write_twice.rs:LL:CC diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index 54ebfa9d198d..c97206487a10 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -6,6 +6,9 @@ use std::convert::TryInto; use std::thread; use std::thread::spawn; +#[path = "../../utils/libc.rs"] +mod libc_utils; + // This is a set of testcases for blocking epoll. fn main() { @@ -97,7 +100,7 @@ fn test_epoll_block_then_unblock() { let thread1 = spawn(move || { thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 10); @@ -130,7 +133,7 @@ fn test_notification_after_timeout() { // Trigger epoll notification after timeout. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Check the result of the notification. diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs index dc3ab2828faa..7130790b86d6 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs @@ -2,6 +2,9 @@ use std::convert::TryInto; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_epoll_socketpair(); test_epoll_socketpair_both_sides(); @@ -64,7 +67,7 @@ fn test_epoll_socketpair() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP @@ -85,7 +88,7 @@ fn test_epoll_socketpair() { // Write some more to fd[0]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // This did not change the readiness of fd[1]. And yet, we're seeing the event reported @@ -153,7 +156,7 @@ fn test_epoll_ctl_del() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLET @@ -182,7 +185,7 @@ fn test_two_epoll_instance() { // Write to the socketpair. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Register one side of the socketpair with EPOLLIN | EPOLLOUT | EPOLLET. @@ -224,7 +227,7 @@ fn test_two_same_fd_in_same_epoll_instance() { // Write to the socketpair. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -243,7 +246,7 @@ fn test_epoll_eventfd() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Create an epoll instance. @@ -282,7 +285,7 @@ fn test_epoll_socketpair_both_sides() { // Write to fds[1]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -297,7 +300,8 @@ fn test_epoll_socketpair_both_sides() { // Read from fds[0]. let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "abcde".as_bytes()); @@ -325,7 +329,7 @@ fn test_closed_fd() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Close the eventfd. @@ -371,7 +375,8 @@ fn test_not_fully_closed_fd() { // Write to the eventfd instance to produce notification. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = + unsafe { libc_utils::write_all(newfd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Close the dupped fd. @@ -391,7 +396,7 @@ fn test_event_overwrite() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); // Create an epoll instance. @@ -445,7 +450,7 @@ fn test_socketpair_read() { // Write 5 bytes to fds[1]. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); //Two notification should be received. @@ -460,7 +465,8 @@ fn test_socketpair_read() { // Read 3 bytes from fds[0]. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 3); assert_eq!(buf, "abc".as_bytes()); @@ -478,7 +484,8 @@ fn test_socketpair_read() { // Read until the buffer is empty. let mut buf: [u8; 2] = [0; 2]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 2); assert_eq!(buf, "de".as_bytes()); @@ -510,8 +517,9 @@ fn test_no_notification_for_unregister_flag() { // Write to fd[1]. let data = "abcde".as_bytes().as_ptr(); - let res: i32 = - unsafe { libc::write(fds[1], data as *const libc::c_void, 5).try_into().unwrap() }; + let res: i32 = unsafe { + libc_utils::write_all(fds[1], data as *const libc::c_void, 5).try_into().unwrap() + }; assert_eq!(res, 5); // Check result from epoll_wait. Since we didn't register EPOLLIN flag, the notification won't @@ -546,7 +554,7 @@ fn test_socketpair_epollerr() { // Write to fd[0] let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); // Close fds[1]. @@ -717,6 +725,6 @@ fn test_issue_3858() { // Write to the eventfd instance. let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = unsafe { libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; + let res = unsafe { libc_utils::write_all(fd, sized_8_data.as_ptr() as *const libc::c_void, 8) }; assert_eq!(res, 8); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 0ff48c389e85..86cf2a041f06 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -14,6 +14,9 @@ use std::path::PathBuf; #[path = "../../utils/mod.rs"] mod utils; +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_dup(); test_dup_stdout_stderr(); @@ -74,8 +77,8 @@ fn test_dup_stdout_stderr() { unsafe { let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0); let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0); - libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); - libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); + libc_utils::write_all(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len()); + libc_utils::write_all(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len()); } } @@ -92,16 +95,24 @@ fn test_dup() { let new_fd2 = libc::dup2(fd, 8); let mut first_buf = [0u8; 4]; - libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&first_buf, b"dup "); + let first_len = libc::read(fd, first_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(first_len > 0); + let first_len = first_len as usize; + assert_eq!(first_buf[..first_len], bytes[..first_len]); + let remaining_bytes = &bytes[first_len..]; let mut second_buf = [0u8; 4]; - libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&second_buf, b"and "); + let second_len = libc::read(new_fd, second_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(second_len > 0); + let second_len = second_len as usize; + assert_eq!(second_buf[..second_len], remaining_bytes[..second_len]); + let remaining_bytes = &remaining_bytes[second_len..]; let mut third_buf = [0u8; 4]; - libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4); - assert_eq!(&third_buf, b"dup2"); + let third_len = libc::read(new_fd2, third_buf.as_mut_ptr() as *mut libc::c_void, 4); + assert!(third_len > 0); + let third_len = third_len as usize; + assert_eq!(third_buf[..third_len], remaining_bytes[..third_len]); } } @@ -145,7 +156,7 @@ fn test_ftruncate>( let bytes = b"hello"; let path = utils::prepare("miri_test_libc_fs_ftruncate.txt"); let mut file = File::create(&path).unwrap(); - file.write(bytes).unwrap(); + file.write_all(bytes).unwrap(); file.sync_all().unwrap(); assert_eq!(file.metadata().unwrap().len(), 5); @@ -402,10 +413,10 @@ fn test_read_and_uninit() { unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); - let mut buf: MaybeUninit<[u8; 2]> = std::mem::MaybeUninit::uninit(); - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 2), 2); + let mut buf: MaybeUninit = std::mem::MaybeUninit::uninit(); + assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 1), 1); let buf = buf.assume_init(); - assert_eq!(buf, [1, 2]); + assert_eq!(buf, 1); assert_eq!(libc::close(fd), 0); } remove_file(&path).unwrap(); @@ -413,14 +424,22 @@ fn test_read_and_uninit() { { // We test that if we requested to read 4 bytes, but actually read 3 bytes, then // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is. - let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &[1u8, 2, 3]); + let data = [1u8, 2, 3]; + let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &data); let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap(); unsafe { let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY); assert_ne!(fd, -1); let mut buf = [42u8; 5]; - assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::(), 4), 3); - assert_eq!(buf, [1, 2, 3, 42, 42]); + let res = libc::read(fd, buf.as_mut_ptr().cast::(), 4); + assert!(res > 0 && res < 4); + for i in 0..buf.len() { + assert_eq!( + buf[i], + if i < res as usize { data[i] } else { 42 }, + "wrong result at pos {i}" + ); + } assert_eq!(libc::close(fd), 0); } remove_file(&path).unwrap(); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs index bc755af864c5..ffbcf633b987 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-pipe.rs @@ -2,6 +2,10 @@ // test_race depends on a deterministic schedule. //@compile-flags: -Zmiri-deterministic-concurrency use std::thread; + +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_pipe(); test_pipe_threaded(); @@ -26,21 +30,29 @@ fn test_pipe() { // Read size == data available in buffer. let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) + }; assert_eq!(res, 5); assert_eq!(buf3, "12345".as_bytes()); // Read size > data available in buffer. - let data = "123".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) }; + let data = "123".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf4: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf4[0..3], "123".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf4[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } } fn test_pipe_threaded() { @@ -51,7 +63,7 @@ fn test_pipe_threaded() { let thread1 = thread::spawn(move || { let mut buf: [u8; 5] = [0; 5]; let res: i64 = unsafe { - libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -60,7 +72,7 @@ fn test_pipe_threaded() { }); thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); thread1.join().unwrap(); @@ -68,11 +80,12 @@ fn test_pipe_threaded() { let thread2 = thread::spawn(move || { thread::yield_now(); let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "12345".as_bytes()); thread2.join().unwrap(); @@ -90,7 +103,7 @@ fn test_race() { // write() from the main thread will occur before the read() here // because preemption is disabled and the main thread yields after write(). let res: i32 = unsafe { - libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -101,7 +114,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data = "a".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 1) }; assert_eq!(res, 1); thread::yield_now(); thread1.join().unwrap(); @@ -186,11 +199,12 @@ fn test_pipe_fcntl_threaded() { // the socket is now "non-blocking", the shim needs to deal correctly // with threads that were blocked before the socket was made non-blocking. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); // The `read` below will block. - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; thread1.join().unwrap(); assert_eq!(res, 5); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs index c36f6b112244..9c211ffbdbe4 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socketpair.rs @@ -6,6 +6,10 @@ #![allow(static_mut_refs)] use std::thread; + +#[path = "../../utils/libc.rs"] +mod libc_utils; + fn main() { test_socketpair(); test_socketpair_threaded(); @@ -22,54 +26,71 @@ fn test_socketpair() { // Read size == data available in buffer. let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "abcde".as_bytes()); // Read size > data available in buffer. - let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let data = "abc".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf2: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[1], buf2.as_mut_ptr().cast(), buf2.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf2[0..3], "abc".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf2[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[1], buf2[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } // Test read and write from another direction. // Read size == data available in buffer. let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); let mut buf3: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[0], buf3.as_mut_ptr().cast(), buf3.len() as libc::size_t) + }; assert_eq!(res, 5); assert_eq!(buf3, "12345".as_bytes()); // Read size > data available in buffer. - let data = "123".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 3) }; + let data = "123".as_bytes(); + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); let mut buf4: [u8; 5] = [0; 5]; let res = unsafe { libc::read(fds[0], buf4.as_mut_ptr().cast(), buf4.len() as libc::size_t) }; - assert_eq!(res, 3); - assert_eq!(&buf4[0..3], "123".as_bytes()); + assert!(res > 0 && res <= 3); + let res = res as usize; + assert_eq!(buf4[..res], data[..res]); + if res < 3 { + // Drain the rest from the read end. + let res = unsafe { libc_utils::read_all(fds[0], buf4[res..].as_mut_ptr().cast(), 3 - res) }; + assert!(res > 0); + } // Test when happens when we close one end, with some data in the buffer. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data.as_ptr() as *const libc::c_void, 3) }; assert_eq!(res, 3); unsafe { libc::close(fds[0]) }; // Reading the other end should return that data, then EOF. let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 3); assert_eq!(&buf[0..3], "123".as_bytes()); - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 0); // 0-sized read: EOF. // Writing the other end should emit EPIPE. - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[1], data.as_ptr() as *const libc::c_void, 1) }; assert_eq!(res, -1); assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPIPE)); } @@ -82,7 +103,7 @@ fn test_socketpair_threaded() { let thread1 = thread::spawn(move || { let mut buf: [u8; 5] = [0; 5]; let res: i64 = unsafe { - libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -91,7 +112,7 @@ fn test_socketpair_threaded() { }); thread::yield_now(); let data = "abcde".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 5) }; assert_eq!(res, 5); thread1.join().unwrap(); @@ -99,11 +120,12 @@ fn test_socketpair_threaded() { let thread2 = thread::spawn(move || { thread::yield_now(); let data = "12345".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) }; + let res = unsafe { libc_utils::write_all(fds[1], data as *const libc::c_void, 5) }; assert_eq!(res, 5); }); let mut buf: [u8; 5] = [0; 5]; - let res = unsafe { libc::read(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = + unsafe { libc_utils::read_all(fds[0], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; assert_eq!(res, 5); assert_eq!(buf, "12345".as_bytes()); thread2.join().unwrap(); @@ -119,7 +141,7 @@ fn test_race() { // write() from the main thread will occur before the read() here // because preemption is disabled and the main thread yields after write(). let res: i32 = unsafe { - libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) .try_into() .unwrap() }; @@ -130,7 +152,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data = "a".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 1) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 1) }; assert_eq!(res, 1); thread::yield_now(); thread1.join().unwrap(); @@ -144,14 +166,16 @@ fn test_blocking_read() { let thread1 = thread::spawn(move || { // Let this thread block on read. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + }; assert_eq!(res, 3); assert_eq!(&buf, "abc".as_bytes()); }); let thread2 = thread::spawn(move || { // Unblock thread1 by doing writing something. let data = "abc".as_bytes().as_ptr(); - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; assert_eq!(res, 3); }); thread1.join().unwrap(); @@ -165,18 +189,21 @@ fn test_blocking_write() { assert_eq!(res, 0); let arr1: [u8; 212992] = [1; 212992]; // Exhaust the space in the buffer so the subsequent write will block. - let res = unsafe { libc::write(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; + let res = + unsafe { libc_utils::write_all(fds[0], arr1.as_ptr() as *const libc::c_void, 212992) }; assert_eq!(res, 212992); let thread1 = thread::spawn(move || { let data = "abc".as_bytes().as_ptr(); // The write below will be blocked because the buffer is already full. - let res = unsafe { libc::write(fds[0], data as *const libc::c_void, 3) }; + let res = unsafe { libc_utils::write_all(fds[0], data as *const libc::c_void, 3) }; assert_eq!(res, 3); }); let thread2 = thread::spawn(move || { // Unblock thread1 by freeing up some space. let mut buf: [u8; 3] = [0; 3]; - let res = unsafe { libc::read(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) }; + let res = unsafe { + libc_utils::read_all(fds[1], buf.as_mut_ptr().cast(), buf.len() as libc::size_t) + }; assert_eq!(res, 3); assert_eq!(buf, [1, 1, 1]); }); diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 5e54cc382381..e7f11c54704d 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -17,6 +17,10 @@ mod utils; fn main() { test_path_conversion(); test_file(); + // Partial reads/writes are apparently not a thing on Windows. + if cfg!(not(windows)) { + test_file_partial_reads_writes(); + } test_file_create_new(); test_metadata(); test_seek(); @@ -53,7 +57,7 @@ fn test_file() { file.write(&mut []).unwrap(); assert_eq!(file.metadata().unwrap().len(), 0); - file.write(bytes).unwrap(); + file.write_all(bytes).unwrap(); assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64); // Test opening, reading and closing a file. let mut file = File::open(&path).unwrap(); @@ -74,6 +78,28 @@ fn test_file() { remove_file(&path).unwrap(); } +fn test_file_partial_reads_writes() { + let path = utils::prepare_with_content("miri_test_fs_file.txt", b"abcdefg"); + + // Ensure we sometimes do incomplete writes. + let got_short_write = (0..16).any(|_| { + let _ = remove_file(&path); // FIXME(win, issue #4483): errors if the file already exists + let mut file = File::create(&path).unwrap(); + file.write(&[0; 4]).unwrap() != 4 + }); + assert!(got_short_write); + // Ensure we sometimes do incomplete reads. + let got_short_read = (0..16).any(|_| { + let mut file = File::open(&path).unwrap(); + let mut buf = [0; 4]; + file.read(&mut buf).unwrap() != 4 + }); + assert!(got_short_read); + + // Clean up + remove_file(&path).unwrap(); +} + fn test_file_clone() { let bytes = b"Hello, World!\n"; let path = utils::prepare_with_content("miri_test_fs_file_clone.txt", bytes); diff --git a/src/tools/miri/tests/pass/shims/pipe.rs b/src/tools/miri/tests/pass/shims/pipe.rs index c47feb8774ad..4915e54c533f 100644 --- a/src/tools/miri/tests/pass/shims/pipe.rs +++ b/src/tools/miri/tests/pass/shims/pipe.rs @@ -4,8 +4,8 @@ use std::io::{Read, Write, pipe}; fn main() { let (mut ping_rx, mut ping_tx) = pipe().unwrap(); - ping_tx.write(b"hello").unwrap(); + ping_tx.write_all(b"hello").unwrap(); let mut buf: [u8; 5] = [0; 5]; - ping_rx.read(&mut buf).unwrap(); + ping_rx.read_exact(&mut buf).unwrap(); assert_eq!(&buf, "hello".as_bytes()); } diff --git a/src/tools/miri/tests/utils/fs.rs b/src/tools/miri/tests/utils/fs.rs index 7340908626fd..7d75b3fced3c 100644 --- a/src/tools/miri/tests/utils/fs.rs +++ b/src/tools/miri/tests/utils/fs.rs @@ -1,6 +1,6 @@ use std::ffi::OsString; -use std::fs; use std::path::PathBuf; +use std::{fs, io}; use super::miri_extern; diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs new file mode 100644 index 000000000000..1a3cd067c04b --- /dev/null +++ b/src/tools/miri/tests/utils/libc.rs @@ -0,0 +1,44 @@ +//! Utils that need libc. +#![allow(dead_code)] + +pub unsafe fn read_all( + fd: libc::c_int, + buf: *mut libc::c_void, + count: libc::size_t, +) -> libc::ssize_t { + assert!(count > 0); + let mut read_so_far = 0; + while read_so_far < count { + let res = libc::read(fd, buf.add(read_so_far), count - read_so_far); + if res < 0 { + return res; + } + if res == 0 { + // EOF + break; + } + read_so_far += res as libc::size_t; + } + return read_so_far as libc::ssize_t; +} + +pub unsafe fn write_all( + fd: libc::c_int, + buf: *const libc::c_void, + count: libc::size_t, +) -> libc::ssize_t { + assert!(count > 0); + let mut written_so_far = 0; + while written_so_far < count { + let res = libc::write(fd, buf.add(written_so_far), count - written_so_far); + if res < 0 { + return res; + } + if res == 0 { + // EOF? + break; + } + written_so_far += res as libc::size_t; + } + return written_so_far as libc::ssize_t; +} From 72ba7c594d3fb9730daa361327aea1a14f096b6e Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Mon, 21 Jul 2025 05:01:50 +0000 Subject: [PATCH 007/201] 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 d734ec333a5c..f6b7efe51a12 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -6707bf0f59485cf054ac1095725df43220e4be20 +460259d14de0274b97b8801e08cb2fe5f16fdac5 From 54fa2ad66e465e3ec536a6b80605ba961b819b63 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Mon, 21 Jul 2025 15:21:01 +0900 Subject: [PATCH 008/201] Migrate AstNodeEdit --- .../src/handlers/convert_bool_then.rs | 3 +- .../src/handlers/convert_to_guarded_return.rs | 8 +-- .../src/handlers/extract_variable.rs | 11 +-- .../src/handlers/generate_delegate_trait.rs | 2 +- .../crates/ide-assists/src/utils.rs | 2 +- .../src/utils/gen_trait_fn_body.rs | 14 ++-- .../crates/syntax/src/ast/edit.rs | 67 +++++++++++++++---- .../crates/syntax/src/ast/syntax_factory.rs | 2 +- 8 files changed, 74 insertions(+), 35 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index bcd06c1ef725..d7b7e8d9cad0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -228,8 +228,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> closure_body, Some(ast::ElseBranch::Block(make.block_expr(None, Some(none_path)))), ) - .indent(mcall.indent_level()) - .clone_for_update(); + .indent(mcall.indent_level()); editor.replace(mcall.syntax().clone(), if_expr.syntax().clone()); editor.add_mappings(make.finish_with_mappings()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 71a61f2db001..bf1cfc74700d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -117,7 +117,7 @@ fn if_expr_to_guarded_return( then_block.syntax().last_child_or_token().filter(|t| t.kind() == T!['}'])?; - let then_block_items = then_block.dedent(IndentLevel(1)).clone_for_update(); + let then_block_items = then_block.dedent(IndentLevel(1)); let end_of_then = then_block_items.syntax().last_child_or_token()?; let end_of_then = if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { @@ -143,7 +143,7 @@ fn if_expr_to_guarded_return( let cond = invert_boolean_expression_legacy(cond_expr); make::expr_if(cond, then_branch, None).indent(if_indent_level) }; - new_expr.syntax().clone_for_update() + new_expr.syntax().clone() } Some(pat) => { // If-let. @@ -154,7 +154,7 @@ fn if_expr_to_guarded_return( ast::make::tail_only_block_expr(early_expression), ); let let_else_stmt = let_else_stmt.indent(if_indent_level); - let_else_stmt.syntax().clone_for_update() + let_else_stmt.syntax().clone() } }; @@ -225,7 +225,7 @@ fn let_stmt_to_guarded_return( ast::make::tail_only_block_expr(early_expression), ); let let_else_stmt = let_else_stmt.indent(let_indent_level); - let_else_stmt.syntax().clone_for_update() + let_else_stmt.syntax().clone() }; ted::replace(let_stmt.syntax(), replacement) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 31e84e9adcf4..db2d316d58ee 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -7,7 +7,9 @@ use syntax::{ NodeOrToken, SyntaxKind, SyntaxNode, T, algo::ancestors_at_offset, ast::{ - self, AstNode, edit::IndentLevel, edit_in_place::Indent, make, + self, AstNode, + edit::{AstNodeEdit, IndentLevel}, + make, syntax_factory::SyntaxFactory, }, syntax_editor::Position, @@ -253,12 +255,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`. editor.replace(expr_replace, name_expr.syntax()); make.block_expr([new_stmt], Some(to_wrap.clone())) - }; + } + // fixup indentation of block + .indent_with_mapping(indent_to, &make); editor.replace(to_wrap.syntax(), block.syntax()); - - // fixup indentation of block - block.indent(indent_to); } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 848c63810a4b..2bdc85e785e9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -767,7 +767,7 @@ fn func_assoc_item( ) .clone_for_update(); - Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)).clone_for_update())) + Some(AssocItem::Fn(func.indent(edit::IndentLevel(1)))) } fn ty_assoc_item(item: syntax::ast::TypeAlias, qual_path_ty: Path) -> Option { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index fbce1d31eaec..35b77d8422b8 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -236,7 +236,7 @@ pub fn add_trait_assoc_items_to_impl( ), new_indent_level, ); - ted::replace(fn_.get_or_create_body().syntax(), body.clone_for_update().syntax()) + ted::replace(fn_.get_or_create_body().syntax(), body.syntax()) } ast::AssocItem::TypeAlias(type_alias) => { if let Some(type_bound_list) = type_alias.type_bound_list() { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c58bdd9e8ede..026209efc446 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -139,7 +139,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } @@ -248,7 +248,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let body = make::block_expr(None, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } @@ -296,7 +296,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let method = make::name_ref("finish"); let expr = make::expr_method_call(expr, method, make::arg_list(None)).into(); let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } } @@ -342,7 +342,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } } @@ -400,7 +400,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { }, }; - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } @@ -595,7 +595,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option }, }; - ted::replace(func.body()?.syntax(), body.clone_for_update().syntax()); + ted::replace(func.body()?.syntax(), body.syntax()); Some(()) } @@ -686,7 +686,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option SyntaxNode { + let node = node.clone_subtree(); + let mut editor = SyntaxEditor::new(node.clone()); + let tokens = node + .preorder_with_tokens() + .filter_map(|event| match event { + rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), + _ => None, + }) + .filter_map(ast::Whitespace::cast) + .filter(|ws| ws.text().contains('\n')); + for ws in tokens { + let new_ws = make::tokens::whitespace(&format!("{}{self}", ws.syntax())); + editor.replace(ws.syntax(), &new_ws); + } + editor.finish().new_root().clone() + } + pub(super) fn decrease_indent(self, node: &SyntaxNode) { let tokens = node.preorder_with_tokens().filter_map(|event| match event { rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), @@ -111,36 +132,54 @@ impl IndentLevel { } } } + + pub(super) fn clone_decrease_indent(self, node: &SyntaxNode) -> SyntaxNode { + let node = node.clone_subtree(); + let mut editor = SyntaxEditor::new(node.clone()); + let tokens = node + .preorder_with_tokens() + .filter_map(|event| match event { + rowan::WalkEvent::Leave(NodeOrToken::Token(it)) => Some(it), + _ => None, + }) + .filter_map(ast::Whitespace::cast) + .filter(|ws| ws.text().contains('\n')); + for ws in tokens { + let new_ws = + make::tokens::whitespace(&ws.syntax().text().replace(&format!("\n{self}"), "\n")); + editor.replace(ws.syntax(), &new_ws); + } + editor.finish().new_root().clone() + } } fn prev_tokens(token: SyntaxToken) -> impl Iterator { iter::successors(Some(token), |token| token.prev_token()) } -/// Soft-deprecated in favor of mutable tree editing API `edit_in_place::Ident`. pub trait AstNodeEdit: AstNode + Clone + Sized { fn indent_level(&self) -> IndentLevel { IndentLevel::from_node(self.syntax()) } #[must_use] fn indent(&self, level: IndentLevel) -> Self { - fn indent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { - let res = node.clone_subtree().clone_for_update(); - level.increase_indent(&res); - res.clone_subtree() + Self::cast(level.clone_increase_indent(self.syntax())).unwrap() + } + #[must_use] + fn indent_with_mapping(&self, level: IndentLevel, make: &SyntaxFactory) -> Self { + let new_node = self.indent(level); + if let Some(mut mapping) = make.mappings() { + let mut builder = SyntaxMappingBuilder::new(new_node.syntax().clone()); + for (old, new) in self.syntax().children().zip(new_node.syntax().children()) { + builder.map_node(old, new); + } + builder.finish(&mut mapping); } - - Self::cast(indent_inner(self.syntax(), level)).unwrap() + new_node } #[must_use] fn dedent(&self, level: IndentLevel) -> Self { - fn dedent_inner(node: &SyntaxNode, level: IndentLevel) -> SyntaxNode { - let res = node.clone_subtree().clone_for_update(); - level.decrease_indent(&res); - res.clone_subtree() - } - - Self::cast(dedent_inner(self.syntax(), level)).unwrap() + Self::cast(level.clone_decrease_indent(self.syntax())).unwrap() } #[must_use] fn reset_indent(&self) -> Self { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs index 7142e4f6e1bc..f3ae7544cc37 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs @@ -38,7 +38,7 @@ impl SyntaxFactory { self.mappings.as_ref().map(|mappings| mappings.take()).unwrap_or_default() } - fn mappings(&self) -> Option> { + pub(crate) fn mappings(&self) -> Option> { self.mappings.as_ref().map(|it| it.borrow_mut()) } } From 8be918734894a1b0fcf4200893aa5fa3a77b3121 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Mon, 21 Jul 2025 01:51:33 +0900 Subject: [PATCH 009/201] migrate generate new --- .../src/handlers/generate_mut_trait_impl.rs | 2 +- .../ide-assists/src/handlers/generate_new.rs | 86 ++++++++++++------- .../crates/ide-assists/src/utils.rs | 18 ++-- .../crates/syntax/src/ast/edit_in_place.rs | 2 +- .../crates/syntax/src/ast/make.rs | 36 ++++---- .../crates/syntax/src/syntax_editor.rs | 11 ++- 6 files changed, 97 insertions(+), 58 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index 4ddab2cfad00..6da5fe180efb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -94,7 +94,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> })?; let _ = process_ref_mut(&fn_); - let assoc_list = make::assoc_item_list().clone_for_update(); + let assoc_list = make::assoc_item_list(None).clone_for_update(); ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_)); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs index 51c2f65e0255..5bda1226cda3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs @@ -4,12 +4,12 @@ use ide_db::{ }; use syntax::{ ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make}, - ted, + syntax_editor::Position, }; use crate::{ AssistContext, AssistId, Assists, - utils::{find_struct_impl, generate_impl}, + utils::{find_struct_impl, generate_impl_with_item}, }; // Assist: generate_new @@ -149,7 +149,53 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option .clone_for_update(); fn_.indent(1.into()); - if let Some(cap) = ctx.config.snippet_cap { + let mut editor = builder.make_editor(strukt.syntax()); + + // Get the node for set annotation + let contain_fn = if let Some(impl_def) = impl_def { + fn_.indent(impl_def.indent_level()); + + if let Some(l_curly) = impl_def.assoc_item_list().and_then(|list| list.l_curly_token()) + { + editor.insert_all( + Position::after(l_curly), + vec![ + make::tokens::whitespace(&format!("\n{}", impl_def.indent_level() + 1)) + .into(), + fn_.syntax().clone().into(), + make::tokens::whitespace("\n").into(), + ], + ); + fn_.syntax().clone() + } else { + let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))]; + let list = make::assoc_item_list(Some(items)); + editor.insert(Position::after(impl_def.syntax()), list.syntax()); + list.syntax().clone() + } + } else { + // Generate a new impl to add the method to + let indent_level = strukt.indent_level(); + let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))]; + let list = make::assoc_item_list(Some(body)); + let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list)); + + impl_def.indent(strukt.indent_level()); + + // Insert it after the adt + editor.insert_all( + Position::after(strukt.syntax()), + vec![ + make::tokens::whitespace(&format!("\n\n{indent_level}")).into(), + impl_def.syntax().clone().into(), + ], + ); + impl_def.syntax().clone() + }; + + if let Some(fn_) = contain_fn.descendants().find_map(ast::Fn::cast) + && let Some(cap) = ctx.config.snippet_cap + { match strukt.kind() { StructKind::Tuple(_) => { let struct_args = fn_ @@ -168,8 +214,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) { if let Some(fn_pat) = fn_param.pat() { let fn_pat = fn_pat.syntax().clone(); - builder - .add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]); + let placeholder = builder.make_placeholder_snippet(cap); + editor.add_annotation_all(vec![struct_arg, fn_pat], placeholder) } } } @@ -179,36 +225,12 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option // Add a tabstop before the name if let Some(name) = fn_.name() { - builder.add_tabstop_before(cap, name); + let tabstop_before = builder.make_tabstop_before(cap); + editor.add_annotation(name.syntax(), tabstop_before); } } - // Get the mutable version of the impl to modify - let impl_def = if let Some(impl_def) = impl_def { - fn_.indent(impl_def.indent_level()); - builder.make_mut(impl_def) - } else { - // Generate a new impl to add the method to - let impl_def = generate_impl(&ast::Adt::Struct(strukt.clone())); - let indent_level = strukt.indent_level(); - fn_.indent(indent_level); - - // Insert it after the adt - let strukt = builder.make_mut(strukt.clone()); - - ted::insert_all_raw( - ted::Position::after(strukt.syntax()), - vec![ - make::tokens::whitespace(&format!("\n\n{indent_level}")).into(), - impl_def.syntax().clone().into(), - ], - ); - - impl_def - }; - - // Add the `new` method at the start of the impl - impl_def.get_or_create_assoc_item_list().add_item_at_start(fn_.into()); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 2c8cb6e4d912..e98f4c0317a5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -663,8 +663,15 @@ fn generate_impl_text_inner( /// Generates the corresponding `impl Type {}` including type and lifetime /// parameters. +pub(crate) fn generate_impl_with_item( + adt: &ast::Adt, + body: Option, +) -> ast::Impl { + generate_impl_inner(adt, None, true, body) +} + pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { - generate_impl_inner(adt, None, true) + generate_impl_inner(adt, None, true, None) } /// Generates the corresponding `impl for Type {}` including type @@ -672,7 +679,7 @@ pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { /// /// This is useful for traits like `PartialEq`, since `impl PartialEq for U` often requires `T: PartialEq`. pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), true) + generate_impl_inner(adt, Some(trait_), true, None) } /// Generates the corresponding `impl for Type {}` including type @@ -680,13 +687,14 @@ pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Imp /// /// This is useful for traits like `From`, since `impl From for U` doesn't require `T: From`. pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), false) + generate_impl_inner(adt, Some(trait_), false, None) } fn generate_impl_inner( adt: &ast::Adt, trait_: Option, trait_is_transitive: bool, + body: Option, ) -> ast::Impl { // Ensure lifetime params are before type & const params let generic_params = adt.generic_param_list().map(|generic_params| { @@ -736,9 +744,9 @@ fn generate_impl_inner( ty, None, adt.where_clause(), - None, + body, ), - None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), None), + None => make::impl_(generic_params, generic_args, ty, adt.where_clause(), body), } .clone_for_update(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs index e902516471d7..28b543ea7064 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs @@ -644,7 +644,7 @@ impl Removable for ast::Use { impl ast::Impl { pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList { if self.assoc_item_list().is_none() { - let assoc_item_list = make::assoc_item_list().clone_for_update(); + let assoc_item_list = make::assoc_item_list(None).clone_for_update(); ted::append_child(self.syntax(), assoc_item_list.syntax()); } self.assoc_item_list().unwrap() diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index d67f24fda96a..66aae101eb00 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -229,8 +229,18 @@ pub fn ty_fn_ptr>( } } -pub fn assoc_item_list() -> ast::AssocItemList { - ast_from_text("impl C for D {}") +pub fn assoc_item_list( + body: Option>>, +) -> ast::AssocItemList { + let is_break_braces = body.is_some(); + let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() }; + let body_indent = if is_break_braces { " ".to_owned() } else { String::new() }; + + let body = match body { + Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + None => String::new(), + }; + ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}")) } fn merge_gen_params( @@ -273,7 +283,7 @@ pub fn impl_( generic_args: Option, path_type: ast::Type, where_clause: Option, - body: Option>>, + body: Option, ) -> ast::Impl { let gen_args = generic_args.map_or_else(String::new, |it| it.to_string()); @@ -281,20 +291,13 @@ pub fn impl_( let body_newline = if where_clause.is_some() && body.is_none() { "\n".to_owned() } else { String::new() }; - let where_clause = match where_clause { Some(pr) => format!("\n{pr}\n"), None => " ".to_owned(), }; - let body = match body { - Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), - None => String::new(), - }; - - ast_from_text(&format!( - "impl{gen_params} {path_type}{gen_args}{where_clause}{{{body_newline}{body}}}" - )) + let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); + ast_from_text(&format!("impl{gen_params} {path_type}{gen_args}{where_clause}{body}")) } pub fn impl_trait( @@ -308,7 +311,7 @@ pub fn impl_trait( ty: ast::Type, trait_where_clause: Option, ty_where_clause: Option, - body: Option>>, + body: Option, ) -> ast::Impl { let is_unsafe = if is_unsafe { "unsafe " } else { "" }; @@ -330,13 +333,10 @@ pub fn impl_trait( let where_clause = merge_where_clause(ty_where_clause, trait_where_clause) .map_or_else(|| " ".to_owned(), |wc| format!("\n{wc}\n")); - let body = match body { - Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), - None => String::new(), - }; + let body = body.map_or_else(|| format!("{{{body_newline}}}"), |it| it.to_string()); ast_from_text(&format!( - "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{{{body_newline}{body}}}" + "{is_unsafe}impl{gen_params} {is_negative}{path_type}{trait_gen_args} for {ty}{type_gen_args}{where_clause}{body}" )) } diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs index 3fa584850f77..5107754b1825 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -5,7 +5,7 @@ //! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs use std::{ - fmt, + fmt, iter, num::NonZeroU32, ops::RangeInclusive, sync::atomic::{AtomicU32, Ordering}, @@ -41,6 +41,15 @@ impl SyntaxEditor { self.annotations.push((element.syntax_element(), annotation)) } + pub fn add_annotation_all( + &mut self, + elements: Vec, + annotation: SyntaxAnnotation, + ) { + self.annotations + .extend(elements.into_iter().map(|e| e.syntax_element()).zip(iter::repeat(annotation))); + } + pub fn merge(&mut self, mut other: SyntaxEditor) { debug_assert!( self.root == other.root || other.root.ancestors().any(|node| node == self.root), From f862bcc7f656a5030771b9297ef54c14f77e81bb Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 21 Jul 2025 16:34:12 +0300 Subject: [PATCH 010/201] Disable tests in flycheck if `cfg.setTest` is set to false --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 2 ++ .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 51d4c29aa74e..9456fd8809b4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2162,6 +2162,7 @@ impl Config { extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), extra_env: self.extra_env(source_root).clone(), target_dir: self.target_dir_from_config(source_root), + set_test: true, } } @@ -2219,6 +2220,7 @@ impl Config { extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), extra_env: self.check_extra_env(source_root), target_dir: self.target_dir_from_config(source_root), + set_test: *self.cfg_setTest(source_root), }, ansi_color_output: self.color_diagnostic_output(), }, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 91d37bd7c9e1..bec57a4beded 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -31,6 +31,7 @@ pub(crate) enum InvocationStrategy { pub(crate) struct CargoOptions { pub(crate) target_tuples: Vec, pub(crate) all_targets: bool, + pub(crate) set_test: bool, pub(crate) no_default_features: bool, pub(crate) all_features: bool, pub(crate) features: Vec, @@ -54,7 +55,13 @@ impl CargoOptions { cmd.args(["--target", target.as_str()]); } if self.all_targets { - cmd.arg("--all-targets"); + if self.set_test { + cmd.arg("--all-targets"); + } else { + // No --benches unfortunately, as this implies --tests (see https://github.com/rust-lang/cargo/issues/6454), + // and users setting `cfg.seTest = false` probably prefer disabling benches than enabling tests. + cmd.args(["--lib", "--bins", "--examples"]); + } } if self.all_features { cmd.arg("--all-features"); From e70112df5f0ff5ebeb4d75d22f66b4b36082c85c Mon Sep 17 00:00:00 2001 From: John Paul Adrian Glaubitz Date: Mon, 21 Jul 2025 17:03:07 +0200 Subject: [PATCH 011/201] hir-def: Don't apply x86_64-specific asserts on x32 This fixes the rustc build on x32 for which struct sizes differ. --- src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs | 2 +- src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs index 19c7ce0ce042..55e738b58bd0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs @@ -27,7 +27,7 @@ pub enum Path { } // This type is being used a lot, make sure it doesn't grow unintentionally. -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] const _: () = { assert!(size_of::() == 24); assert!(size_of::>() == 24); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs index eacc3f3cedfe..da0f058a9cb5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs @@ -148,7 +148,7 @@ pub enum TypeRef { Error, } -#[cfg(target_arch = "x86_64")] +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] const _: () = assert!(size_of::() == 24); pub type TypeRefId = Idx; From f1823d15213c3dbafb3d3d39b755ce30da070ef3 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Tue, 22 Jul 2025 00:17:09 +0900 Subject: [PATCH 012/201] fix: Apply adjusts to pats and exprs when doing pat analysis --- .../crates/hir-ty/src/diagnostics/expr.rs | 85 ++++++++++--------- .../rust-analyzer/crates/hir-ty/src/infer.rs | 26 ++++++ .../crates/hir/src/source_analyzer.rs | 9 +- .../src/handlers/non_exhaustive_let.rs | 24 ++++++ 4 files changed, 95 insertions(+), 49 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 5ae6bf6dffd7..cc531f076dd1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -175,8 +175,9 @@ impl ExprValidator { }); } - let receiver_ty = self.infer[*receiver].clone(); - checker.prev_receiver_ty = Some(receiver_ty); + if let Some(receiver_ty) = self.infer.type_of_expr_with_adjust(*receiver) { + checker.prev_receiver_ty = Some(receiver_ty.clone()); + } } } @@ -187,7 +188,9 @@ impl ExprValidator { arms: &[MatchArm], db: &dyn HirDatabase, ) { - let scrut_ty = &self.infer[scrutinee_expr]; + let Some(scrut_ty) = self.infer.type_of_expr_with_adjust(scrutinee_expr) else { + return; + }; if scrut_ty.contains_unknown() { return; } @@ -200,7 +203,7 @@ impl ExprValidator { // Note: Skipping the entire diagnostic rather than just not including a faulty match arm is // preferred to avoid the chance of false positives. for arm in arms { - let Some(pat_ty) = self.infer.type_of_pat.get(arm.pat) else { + let Some(pat_ty) = self.infer.type_of_pat_with_adjust(arm.pat) else { return; }; if pat_ty.contains_unknown() { @@ -328,7 +331,7 @@ impl ExprValidator { continue; } let Some(initializer) = initializer else { continue }; - let ty = &self.infer[initializer]; + let Some(ty) = self.infer.type_of_expr_with_adjust(initializer) else { continue }; if ty.contains_unknown() { continue; } @@ -433,44 +436,44 @@ impl ExprValidator { Statement::Expr { expr, .. } => Some(*expr), _ => None, }); - if let Some(last_then_expr) = last_then_expr { - let last_then_expr_ty = &self.infer[last_then_expr]; - if last_then_expr_ty.is_never() { - // Only look at sources if the then branch diverges and we have an else branch. - let source_map = db.body_with_source_map(self.owner).1; - let Ok(source_ptr) = source_map.expr_syntax(id) else { - return; - }; - let root = source_ptr.file_syntax(db); - let either::Left(ast::Expr::IfExpr(if_expr)) = - source_ptr.value.to_node(&root) - else { - return; - }; - let mut top_if_expr = if_expr; - loop { - let parent = top_if_expr.syntax().parent(); - let has_parent_expr_stmt_or_stmt_list = - parent.as_ref().is_some_and(|node| { - ast::ExprStmt::can_cast(node.kind()) - | ast::StmtList::can_cast(node.kind()) - }); - if has_parent_expr_stmt_or_stmt_list { - // Only emit diagnostic if parent or direct ancestor is either - // an expr stmt or a stmt list. - break; - } - let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else { - // Bail if parent is neither an if expr, an expr stmt nor a stmt list. - return; - }; - // Check parent if expr. - top_if_expr = parent_if_expr; + if let Some(last_then_expr) = last_then_expr + && let Some(last_then_expr_ty) = + self.infer.type_of_expr_with_adjust(last_then_expr) + && last_then_expr_ty.is_never() + { + // Only look at sources if the then branch diverges and we have an else branch. + let source_map = db.body_with_source_map(self.owner).1; + let Ok(source_ptr) = source_map.expr_syntax(id) else { + return; + }; + let root = source_ptr.file_syntax(db); + let either::Left(ast::Expr::IfExpr(if_expr)) = source_ptr.value.to_node(&root) + else { + return; + }; + let mut top_if_expr = if_expr; + loop { + let parent = top_if_expr.syntax().parent(); + let has_parent_expr_stmt_or_stmt_list = + parent.as_ref().is_some_and(|node| { + ast::ExprStmt::can_cast(node.kind()) + | ast::StmtList::can_cast(node.kind()) + }); + if has_parent_expr_stmt_or_stmt_list { + // Only emit diagnostic if parent or direct ancestor is either + // an expr stmt or a stmt list. + break; } - - self.diagnostics - .push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id }) + let Some(parent_if_expr) = parent.and_then(ast::IfExpr::cast) else { + // Bail if parent is neither an if expr, an expr stmt nor a stmt list. + return; + }; + // Check parent if expr. + top_if_expr = parent_if_expr; } + + self.diagnostics + .push(BodyValidationDiagnostic::RemoveUnnecessaryElse { if_expr: id }) } } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index e880438e3a78..7c39afa0ef89 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -561,6 +561,32 @@ impl InferenceResult { ExprOrPatId::PatId(id) => self.type_of_pat.get(id), } } + pub fn type_of_expr_with_adjust(&self, id: ExprId) -> Option<&Ty> { + match self.expr_adjustments.get(&id).and_then(|adjustments| { + adjustments + .iter() + .filter(|adj| { + // https://github.com/rust-lang/rust/blob/67819923ac8ea353aaa775303f4c3aacbf41d010/compiler/rustc_mir_build/src/thir/cx/expr.rs#L140 + !matches!( + adj, + Adjustment { + kind: Adjust::NeverToAny, + target, + } if target.is_never() + ) + }) + .next_back() + }) { + Some(adjustment) => Some(&adjustment.target), + None => self.type_of_expr.get(id), + } + } + pub fn type_of_pat_with_adjust(&self, id: PatId) -> Option<&Ty> { + match self.pat_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { + adjusted @ Some(_) => adjusted, + None => self.type_of_pat.get(id), + } + } pub fn is_erroneous(&self) -> bool { self.has_errors && self.type_of_expr.iter().count() == 0 } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index ecc6e5f3d038..0b554a9d4e37 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -441,7 +441,7 @@ impl<'db> SourceAnalyzer<'db> { ) -> Option> { let body = self.store()?; if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { - let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?; + let (adt, subst) = infer.type_of_expr_with_adjust(object_expr)?.as_adt()?; return Some(GenericSubstitution::new( adt.into(), subst.clone(), @@ -1780,10 +1780,3 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let ctx = span_map.span_at(name.value.text_range().start()).ctx; HygieneId::new(ctx.opaque_and_semitransparent(db)) } - -fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> { - match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) { - Some(adjustment) => Some(&adjustment.target), - None => Some(&infer[id]), - } -} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs index f20b6dea1227..e31367f3b14e 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs @@ -131,4 +131,28 @@ fn foo(v: Enum<()>) { "#, ); } + + #[test] + fn regression_20259() { + check_diagnostics( + r#" +//- minicore: deref +use core::ops::Deref; + +struct Foo(T); + +impl Deref for Foo { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn test(x: Foo<(i32, bool)>) { + let (_a, _b): &(i32, bool) = &x; +} +"#, + ); + } } From 6f06667ba9a46852dd4f0d2a390e74a39858353d Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Tue, 22 Jul 2025 04:59:16 +0000 Subject: [PATCH 013/201] 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 f6b7efe51a12..bd902ca1fd5a 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -460259d14de0274b97b8801e08cb2fe5f16fdac5 +9748d87dc70a9a6725c5dbd76ce29d04752b4f90 From 1e1e5dee5c3bfc6feb674c9855969372f977f8b1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 05:30:29 +0000 Subject: [PATCH 014/201] Bump form-data from 4.0.2 to 4.0.4 in /editors/code Bumps [form-data](https://github.com/form-data/form-data) from 4.0.2 to 4.0.4. - [Release notes](https://github.com/form-data/form-data/releases) - [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md) - [Commits](https://github.com/form-data/form-data/compare/v4.0.2...v4.0.4) --- updated-dependencies: - dependency-name: form-data dependency-version: 4.0.4 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- src/tools/rust-analyzer/editors/code/package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/package-lock.json b/src/tools/rust-analyzer/editors/code/package-lock.json index 57d67a69b2e7..534c24be52e8 100644 --- a/src/tools/rust-analyzer/editors/code/package-lock.json +++ b/src/tools/rust-analyzer/editors/code/package-lock.json @@ -3336,15 +3336,16 @@ } }, "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { From fa95cba0f00ab263a089b6b34396c892a6d49f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 22 Jul 2025 11:22:16 +0300 Subject: [PATCH 015/201] Support filtering in analysis-stats MIR lowering --- .../rust-analyzer/src/cli/analysis_stats.rs | 128 +++++++++--------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index fc89f486f849..4f75d14834c6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -656,22 +656,26 @@ impl flags::AnalysisStats { let mut sw = self.stop_watch(); let mut all = 0; let mut fail = 0; - for &body in bodies { - if matches!(body, DefWithBody::Variant(_)) { + for &body_id in bodies { + if matches!(body_id, DefWithBody::Variant(_)) { continue; } + let module = body_id.module(db); + if !self.should_process(db, body_id, module) { + continue; + } + all += 1; - let Err(e) = db.mir_body(body.into()) else { + let Err(e) = db.mir_body(body_id.into()) else { continue; }; if verbosity.is_spammy() { - let full_name = body - .module(db) + let full_name = module .path_to_root(db) .into_iter() .rev() .filter_map(|it| it.name(db)) - .chain(Some(body.name(db).unwrap_or_else(Name::missing))) + .chain(Some(body_id.name(db).unwrap_or_else(Name::missing))) .map(|it| it.display(db, Edition::LATEST).to_string()) .join("::"); bar.println(format!("Mir body for {full_name} failed due {e:?}")); @@ -727,26 +731,9 @@ impl flags::AnalysisStats { let name = body_id.name(db).unwrap_or_else(Name::missing); let module = body_id.module(db); let display_target = module.krate().to_display_target(db); - let full_name = move || { - module - .krate() - .display_name(db) - .map(|it| it.canonical_name().as_str().to_owned()) - .into_iter() - .chain( - module - .path_to_root(db) - .into_iter() - .filter_map(|it| it.name(db)) - .rev() - .chain(Some(body_id.name(db).unwrap_or_else(Name::missing))) - .map(|it| it.display(db, Edition::LATEST).to_string()), - ) - .join("::") - }; if let Some(only_name) = self.only.as_deref() { if name.display(db, Edition::LATEST).to_string() != only_name - && full_name() != only_name + && full_name(db, body_id, module) != only_name { continue; } @@ -763,12 +750,17 @@ impl flags::AnalysisStats { let original_file = src.file_id.original_file(db); let path = vfs.file_path(original_file.file_id(db)); let syntax_range = src.text_range(); - format!("processing: {} ({} {:?})", full_name(), path, syntax_range) + format!( + "processing: {} ({} {:?})", + full_name(db, body_id, module), + path, + syntax_range + ) } else { - format!("processing: {}", full_name()) + format!("processing: {}", full_name(db, body_id, module)) } } else { - format!("processing: {}", full_name()) + format!("processing: {}", full_name(db, body_id, module)) } }; if verbosity.is_spammy() { @@ -781,9 +773,11 @@ impl flags::AnalysisStats { Ok(inference_result) => inference_result, Err(p) => { if let Some(s) = p.downcast_ref::<&str>() { - eprintln!("infer panicked for {}: {}", full_name(), s); + eprintln!("infer panicked for {}: {}", full_name(db, body_id, module), s); } else if let Some(s) = p.downcast_ref::() { - eprintln!("infer panicked for {}: {}", full_name(), s); + eprintln!("infer panicked for {}: {}", full_name(db, body_id, module), s); + } else { + eprintln!("infer panicked for {}", full_name(db, body_id, module)); } panics += 1; bar.inc(1); @@ -890,7 +884,7 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { bar.println(format!( "In {}: {} exprs, {} unknown, {} partial", - full_name(), + full_name(db, body_id, module), num_exprs - previous_exprs, num_exprs_unknown - previous_unknown, num_exprs_partially_unknown - previous_partially_unknown @@ -993,7 +987,7 @@ impl flags::AnalysisStats { if verbosity.is_spammy() { bar.println(format!( "In {}: {} pats, {} unknown, {} partial", - full_name(), + full_name(db, body_id, module), num_pats - previous_pats, num_pats_unknown - previous_unknown, num_pats_partially_unknown - previous_partially_unknown @@ -1049,34 +1043,8 @@ impl flags::AnalysisStats { bar.tick(); for &body_id in bodies { let module = body_id.module(db); - let full_name = move || { - module - .krate() - .display_name(db) - .map(|it| it.canonical_name().as_str().to_owned()) - .into_iter() - .chain( - module - .path_to_root(db) - .into_iter() - .filter_map(|it| it.name(db)) - .rev() - .chain(Some(body_id.name(db).unwrap_or_else(Name::missing))) - .map(|it| it.display(db, Edition::LATEST).to_string()), - ) - .join("::") - }; - if let Some(only_name) = self.only.as_deref() { - if body_id - .name(db) - .unwrap_or_else(Name::missing) - .display(db, Edition::LATEST) - .to_string() - != only_name - && full_name() != only_name - { - continue; - } + if !self.should_process(db, body_id, module) { + continue; } let msg = move || { if verbosity.is_verbose() { @@ -1090,12 +1058,17 @@ impl flags::AnalysisStats { let original_file = src.file_id.original_file(db); let path = vfs.file_path(original_file.file_id(db)); let syntax_range = src.text_range(); - format!("processing: {} ({} {:?})", full_name(), path, syntax_range) + format!( + "processing: {} ({} {:?})", + full_name(db, body_id, module), + path, + syntax_range + ) } else { - format!("processing: {}", full_name()) + format!("processing: {}", full_name(db, body_id, module)) } } else { - format!("processing: {}", full_name()) + format!("processing: {}", full_name(db, body_id, module)) } }; if verbosity.is_spammy() { @@ -1205,11 +1178,42 @@ impl flags::AnalysisStats { eprintln!("{:<20} {} ({} files)", "IDE:", ide_time, file_ids.len()); } + fn should_process(&self, db: &RootDatabase, body_id: DefWithBody, module: hir::Module) -> bool { + if let Some(only_name) = self.only.as_deref() { + let name = body_id.name(db).unwrap_or_else(Name::missing); + + if name.display(db, Edition::LATEST).to_string() != only_name + && full_name(db, body_id, module) != only_name + { + return false; + } + } + true + } + fn stop_watch(&self) -> StopWatch { StopWatch::start() } } +fn full_name(db: &RootDatabase, body_id: DefWithBody, module: hir::Module) -> String { + module + .krate() + .display_name(db) + .map(|it| it.canonical_name().as_str().to_owned()) + .into_iter() + .chain( + module + .path_to_root(db) + .into_iter() + .filter_map(|it| it.name(db)) + .rev() + .chain(Some(body_id.name(db).unwrap_or_else(Name::missing))) + .map(|it| it.display(db, Edition::LATEST).to_string()), + ) + .join("::") +} + fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id: ExprId) -> String { let src = match sm.expr_syntax(expr_id) { Ok(s) => s, From eabf33a0051443bdfb59a2ec6f93fd302d1429db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 22 Jul 2025 15:56:41 +0300 Subject: [PATCH 016/201] Add josh-sync.toml --- src/tools/rust-analyzer/josh-sync.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/tools/rust-analyzer/josh-sync.toml diff --git a/src/tools/rust-analyzer/josh-sync.toml b/src/tools/rust-analyzer/josh-sync.toml new file mode 100644 index 000000000000..51ff0d71e71a --- /dev/null +++ b/src/tools/rust-analyzer/josh-sync.toml @@ -0,0 +1,2 @@ +repo = "rust-analyzer" +filter = ":rev(55d9a533b309119c8acd13061581b43ae8840823:prefix=src/tools/rust-analyzer):/src/tools/rust-analyzer" From 2bd169fcc7c47c324fc4a1b3dd42bc7a2ab45b0a Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Tue, 22 Jul 2025 16:24:42 +0300 Subject: [PATCH 017/201] Parse `for<'a> [const]` And also refactor parsing of HRTB. --- .../crates/hir-def/src/expr_store/lower.rs | 41 +++---- .../hir-def/src/expr_store/lower/generics.rs | 23 ++-- .../crates/ide/src/inlay_hints/lifetime.rs | 18 +-- .../parser/src/grammar/expressions/atom.rs | 2 - .../parser/src/grammar/generic_params.rs | 89 +++++++------- .../crates/parser/src/grammar/types.rs | 5 +- .../parser/src/syntax_kind/generated.rs | 4 +- .../parser/test_data/generated/runner.rs | 8 ++ .../parser/err/0024_many_type_parens.rast | 69 +++++------ .../parser/err/0027_incomplete_where_for.rast | 15 +-- .../parser/err/0043_unexpected_for_type.rast | 96 ++++++++------- .../parser/inline/ok/closure_binder.rast | 2 +- .../parser/inline/ok/dyn_trait_type_weak.rast | 14 +-- .../parser/inline/ok/for_binder_bound.rast | 45 +++++++ .../parser/inline/ok/for_binder_bound.rs | 1 + .../test_data/parser/inline/ok/for_type.rast | 45 +++---- .../parser/inline/ok/lambda_expr.rast | 4 +- .../inline/ok/maybe_const_trait_bound.rast | 36 ++++++ .../inline/ok/maybe_const_trait_bound.rs | 1 + .../inline/ok/no_dyn_trait_leading_for.rast | 15 +-- .../ok/question_for_type_trait_bound.rast | 9 +- .../parser/inline/ok/where_pred_for.rast | 15 +-- .../test_data/parser/ok/0032_where_for.rast | 26 ++-- .../parser/ok/0067_where_for_pred.rast | 97 ++++++++------- .../rust-analyzer/crates/syntax/rust.ungram | 10 +- .../rust-analyzer/crates/syntax/src/ast.rs | 9 +- .../crates/syntax/src/ast/generated/nodes.rs | 112 +++++++++--------- .../crates/syntax/src/ast/node_ext.rs | 8 +- 28 files changed, 465 insertions(+), 354 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rs create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rast create mode 100644 src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rs diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 4e877748ca2a..7990a6436ad0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -960,37 +960,28 @@ impl ExprCollector<'_> { impl_trait_lower_fn: ImplTraitLowerFn<'_>, ) -> TypeBound { match node.kind() { - ast::TypeBoundKind::PathType(path_type) => { - let m = match node.question_mark_token() { - Some(_) => TraitBoundModifier::Maybe, - None => TraitBoundModifier::None, - }; - self.lower_path_type(&path_type, impl_trait_lower_fn) - .map(|p| { - TypeBound::Path(self.alloc_path(p, AstPtr::new(&path_type).upcast()), m) - }) - .unwrap_or(TypeBound::Error) - } - ast::TypeBoundKind::ForType(for_type) => { - let lt_refs = match for_type.generic_param_list() { + ast::TypeBoundKind::PathType(binder, path_type) => { + let binder = match binder.and_then(|it| it.generic_param_list()) { Some(gpl) => gpl .lifetime_params() .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) .collect(), None => ThinVec::default(), }; - let path = for_type.ty().and_then(|ty| match &ty { - ast::Type::PathType(path_type) => { - self.lower_path_type(path_type, impl_trait_lower_fn).map(|p| (p, ty)) - } - _ => None, - }); - match path { - Some((p, ty)) => { - TypeBound::ForLifetime(lt_refs, self.alloc_path(p, AstPtr::new(&ty))) - } - None => TypeBound::Error, - } + let m = match node.question_mark_token() { + Some(_) => TraitBoundModifier::Maybe, + None => TraitBoundModifier::None, + }; + self.lower_path_type(&path_type, impl_trait_lower_fn) + .map(|p| { + let path = self.alloc_path(p, AstPtr::new(&path_type).upcast()); + if binder.is_empty() { + TypeBound::Path(path, m) + } else { + TypeBound::ForLifetime(binder, path) + } + }) + .unwrap_or(TypeBound::Error) } ast::TypeBoundKind::Use(gal) => TypeBound::Use( gal.use_bound_generic_args() diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs index 02a1d274fb5e..c570df42b2f6 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/generics.rs @@ -180,17 +180,18 @@ impl GenericParamsCollector { continue; }; - let lifetimes: Option> = pred.generic_param_list().map(|param_list| { - // Higher-Ranked Trait Bounds - param_list - .lifetime_params() - .map(|lifetime_param| { - lifetime_param - .lifetime() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) - }) - .collect() - }); + let lifetimes: Option> = + pred.for_binder().and_then(|it| it.generic_param_list()).map(|param_list| { + // Higher-Ranked Trait Bounds + param_list + .lifetime_params() + .map(|lifetime_param| { + lifetime_param + .lifetime() + .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) + }) + .collect() + }); for bound in pred.type_bound_list().iter().flat_map(|l| l.bounds()) { self.lower_type_bound_as_predicate(ec, bound, lifetimes.as_deref(), target); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs index 0069452e7b90..49fec0a793c3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -77,17 +77,18 @@ pub(super) fn fn_ptr_hints( return None; } - let parent_for_type = func + let parent_for_binder = func .syntax() .ancestors() .skip(1) .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) - .find_map(ast::ForType::cast); + .find_map(ast::ForType::cast) + .and_then(|it| it.for_binder()); let param_list = func.param_list()?; - let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); + let generic_param_list = parent_for_binder.as_ref().and_then(|it| it.generic_param_list()); let ret_type = func.ret_type(); - let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + let for_kw = parent_for_binder.as_ref().and_then(|it| it.for_token()); hints_( acc, ctx, @@ -143,15 +144,16 @@ pub(super) fn fn_path_hints( // FIXME: Support general path types let (param_list, ret_type) = func.path().as_ref().and_then(path_as_fn)?; - let parent_for_type = func + let parent_for_binder = func .syntax() .ancestors() .skip(1) .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) - .find_map(ast::ForType::cast); + .find_map(ast::ForType::cast) + .and_then(|it| it.for_binder()); - let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); - let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + let generic_param_list = parent_for_binder.as_ref().and_then(|it| it.generic_param_list()); + let for_kw = parent_for_binder.as_ref().and_then(|it| it.for_token()); hints_( acc, ctx, diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index 76656567e7f2..ed8a91c39c01 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -572,9 +572,7 @@ fn closure_expr(p: &mut Parser<'_>) -> CompletedMarker { // test closure_binder // fn main() { for<'a> || (); } if p.at(T![for]) { - let b = p.start(); types::for_binder(p); - b.complete(p, CLOSURE_BINDER); } // test const_closure // fn main() { let cl = const || _ = 0; } diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index 55c5dc400b9e..cb1b59f64977 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -13,7 +13,7 @@ pub(super) fn opt_generic_param_list(p: &mut Parser<'_>) { // test_err generic_param_list_recover // fn f() {} -fn generic_param_list(p: &mut Parser<'_>) { +pub(super) fn generic_param_list(p: &mut Parser<'_>) { assert!(p.at(T![<])); let m = p.start(); delimited( @@ -147,7 +147,15 @@ fn type_bound(p: &mut Parser<'_>) -> bool { let has_paren = p.eat(T!['(']); match p.current() { LIFETIME_IDENT => lifetime(p), - T![for] => types::for_type(p, false), + // test for_binder_bound + // fn foo [const] async Trait>() {} + T![for] => { + types::for_binder(p); + if path_type_bound(p).is_err() { + m.abandon(p); + return false; + } + } // test precise_capturing // fn captures<'a: 'a, 'b: 'b, T>() -> impl Sized + use<'b, T, Self> {} @@ -180,44 +188,8 @@ fn type_bound(p: &mut Parser<'_>) -> bool { p.bump_any(); types::for_type(p, false) } - current => { - match current { - T![?] => p.bump_any(), - T![~] => { - p.bump_any(); - p.expect(T![const]); - } - T!['['] => { - p.bump_any(); - p.expect(T![const]); - p.expect(T![']']); - } - // test const_trait_bound - // const fn foo(_: impl const Trait) {} - T![const] => { - p.bump_any(); - } - // test async_trait_bound - // fn async_foo(_: impl async Fn(&i32)) {} - T![async] => { - p.bump_any(); - } - _ => (), - } - if paths::is_use_path_start(p) { - types::path_type_bounds(p, false); - // test_err type_bounds_macro_call_recovery - // fn foo() -> Box {} - if p.at(T![!]) { - let m = p.start(); - p.bump(T![!]); - p.error("unexpected `!` in type path, macro calls are not allowed here"); - if p.at_ts(TokenSet::new(&[T!['{'], T!['['], T!['(']])) { - items::token_tree(p); - } - m.complete(p, ERROR); - } - } else { + _ => { + if path_type_bound(p).is_err() { m.abandon(p); return false; } @@ -231,6 +203,43 @@ fn type_bound(p: &mut Parser<'_>) -> bool { true } +fn path_type_bound(p: &mut Parser<'_>) -> Result<(), ()> { + if p.eat(T![~]) { + p.expect(T![const]); + } else if p.eat(T!['[']) { + // test maybe_const_trait_bound + // const fn foo(_: impl [const] Trait) {} + p.expect(T![const]); + p.expect(T![']']); + } else { + // test const_trait_bound + // const fn foo(_: impl const Trait) {} + p.eat(T![const]); + } + // test async_trait_bound + // fn async_foo(_: impl async Fn(&i32)) {} + p.eat(T![async]); + p.eat(T![?]); + + if paths::is_use_path_start(p) { + types::path_type_bounds(p, false); + // test_err type_bounds_macro_call_recovery + // fn foo() -> Box {} + if p.at(T![!]) { + let m = p.start(); + p.bump(T![!]); + p.error("unexpected `!` in type path, macro calls are not allowed here"); + if p.at_ts(TokenSet::new(&[T!['{'], T!['['], T!['(']])) { + items::token_tree(p); + } + m.complete(p, ERROR); + } + Ok(()) + } else { + Err(()) + } +} + // test where_clause // fn foo() // where diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs index 908440b5d056..a7e97c5f8508 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/types.rs @@ -249,13 +249,14 @@ fn fn_ptr_type(p: &mut Parser<'_>) { } pub(super) fn for_binder(p: &mut Parser<'_>) { - assert!(p.at(T![for])); + let m = p.start(); p.bump(T![for]); if p.at(T![<]) { - generic_params::opt_generic_param_list(p); + generic_params::generic_param_list(p); } else { p.error("expected `<`"); } + m.complete(p, FOR_BINDER); } // test for_type diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 12a13caa4d91..3a8041d2df9e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -185,7 +185,6 @@ pub enum SyntaxKind { BREAK_EXPR, CALL_EXPR, CAST_EXPR, - CLOSURE_BINDER, CLOSURE_EXPR, CONST, CONST_ARG, @@ -203,6 +202,7 @@ pub enum SyntaxKind { FN_PTR_TYPE, FORMAT_ARGS_ARG, FORMAT_ARGS_EXPR, + FOR_BINDER, FOR_EXPR, FOR_TYPE, GENERIC_ARG_LIST, @@ -358,7 +358,6 @@ impl SyntaxKind { | BREAK_EXPR | CALL_EXPR | CAST_EXPR - | CLOSURE_BINDER | CLOSURE_EXPR | CONST | CONST_ARG @@ -376,6 +375,7 @@ impl SyntaxKind { | FN_PTR_TYPE | FORMAT_ARGS_ARG | FORMAT_ARGS_EXPR + | FOR_BINDER | FOR_EXPR | FOR_TYPE | GENERIC_ARG_LIST diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index cef7b0ee2397..c642e1a3354f 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -253,6 +253,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/fn_pointer_unnamed_arg.rs"); } #[test] + fn for_binder_bound() { + run_and_expect_no_errors("test_data/parser/inline/ok/for_binder_bound.rs"); + } + #[test] fn for_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/for_expr.rs"); } #[test] fn for_range_from() { @@ -402,6 +406,10 @@ mod ok { #[test] fn match_guard() { run_and_expect_no_errors("test_data/parser/inline/ok/match_guard.rs"); } #[test] + fn maybe_const_trait_bound() { + run_and_expect_no_errors("test_data/parser/inline/ok/maybe_const_trait_bound.rs"); + } + #[test] fn metas() { run_and_expect_no_errors("test_data/parser/inline/ok/metas.rs"); } #[test] fn method_call_expr() { diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast index 025c12e4c2a0..2fd172539e41 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0024_many_type_parens.rast @@ -37,7 +37,7 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND L_PAREN "(" - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -45,18 +45,18 @@ SOURCE_FILE LIFETIME LIFETIME_IDENT "'a" R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Trait" - GENERIC_ARG_LIST - L_ANGLE "<" - LIFETIME_ARG - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" R_PAREN ")" R_ANGLE ">" PARAM_LIST @@ -124,7 +124,7 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND L_PAREN "(" - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -132,18 +132,18 @@ SOURCE_FILE LIFETIME LIFETIME_IDENT "'a" R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Trait" - GENERIC_ARG_LIST - L_ANGLE "<" - LIFETIME_ARG - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" R_PAREN ")" ERROR R_ANGLE ">" @@ -186,7 +186,7 @@ SOURCE_FILE TUPLE_EXPR L_PAREN "(" CLOSURE_EXPR - CLOSURE_BINDER + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -243,13 +243,14 @@ SOURCE_FILE PAREN_TYPE L_PAREN "(" FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast index 674c8d536cac..3768a55d5308 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0027_incomplete_where_for.rast @@ -12,13 +12,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE " " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE "\n" BLOCK_EXPR STMT_LIST diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0043_unexpected_for_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0043_unexpected_for_type.rast index cb4fb1642d95..9c4ee6f712ad 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0043_unexpected_for_type.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0043_unexpected_for_type.rast @@ -8,13 +8,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " REF_TYPE AMP "&" @@ -37,13 +38,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " TUPLE_TYPE L_PAREN "(" @@ -70,13 +72,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " SLICE_TYPE L_BRACK "[" @@ -97,22 +100,24 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" - WHITESPACE " " - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" LIFETIME_PARAM LIFETIME - LIFETIME_IDENT "'b" + LIFETIME_IDENT "'a" R_ANGLE ">" + WHITESPACE " " + FOR_TYPE + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'b" + R_ANGLE ">" WHITESPACE " " FN_PTR_TYPE FN_KW "fn" @@ -164,31 +169,34 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" - WHITESPACE " " - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" LIFETIME_PARAM LIFETIME - LIFETIME_IDENT "'b" + LIFETIME_IDENT "'a" R_ANGLE ">" - WHITESPACE " " - FOR_TYPE + WHITESPACE " " + FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" LIFETIME_PARAM LIFETIME - LIFETIME_IDENT "'c" + LIFETIME_IDENT "'b" R_ANGLE ">" + WHITESPACE " " + FOR_TYPE + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'c" + R_ANGLE ">" WHITESPACE " " FN_PTR_TYPE FN_KW "fn" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast index c04dbe1ea0a4..c96ccf7c7f1e 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/closure_binder.rast @@ -14,7 +14,7 @@ SOURCE_FILE WHITESPACE " " EXPR_STMT CLOSURE_EXPR - CLOSURE_BINDER + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast index dcc66dc1e2b6..6578809cb0e0 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast @@ -103,7 +103,7 @@ SOURCE_FILE WHITESPACE " " TYPE_BOUND_LIST TYPE_BOUND - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -111,12 +111,12 @@ SOURCE_FILE LIFETIME LIFETIME_IDENT "'a" R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Path" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Path" SEMICOLON ";" WHITESPACE "\n" TYPE_ALIAS diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rast new file mode 100644 index 000000000000..17dbbf30a7bc --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rast @@ -0,0 +1,45 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + GENERIC_PARAM_LIST + L_ANGLE "<" + TYPE_PARAM + NAME + IDENT "T" + COLON ":" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" + WHITESPACE " " + L_BRACK "[" + CONST_KW "const" + R_BRACK "]" + WHITESPACE " " + ASYNC_KW "async" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + R_ANGLE ">" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rs new file mode 100644 index 000000000000..427cf558710d --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_binder_bound.rs @@ -0,0 +1 @@ +fn foo [const] async Trait>() {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast index 7600457a9b8b..58623058cae1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/for_type.rast @@ -8,13 +8,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " FN_PTR_TYPE FN_KW "fn" @@ -39,13 +40,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " FN_PTR_TYPE UNSAFE_KW "unsafe" @@ -86,13 +88,14 @@ SOURCE_FILE EQ "=" WHITESPACE " " FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast index ea401d224e69..bf24a579124e 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/lambda_expr.rast @@ -202,7 +202,7 @@ SOURCE_FILE WHITESPACE "\n " EXPR_STMT CLOSURE_EXPR - CLOSURE_BINDER + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -223,7 +223,7 @@ SOURCE_FILE WHITESPACE "\n " EXPR_STMT CLOSURE_EXPR - CLOSURE_BINDER + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rast new file mode 100644 index 000000000000..8d12f814c2a3 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rast @@ -0,0 +1,36 @@ +SOURCE_FILE + FN + CONST_KW "const" + WHITESPACE " " + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + PARAM + WILDCARD_PAT + UNDERSCORE "_" + COLON ":" + WHITESPACE " " + IMPL_TRAIT_TYPE + IMPL_KW "impl" + WHITESPACE " " + TYPE_BOUND_LIST + TYPE_BOUND + L_BRACK "[" + CONST_KW "const" + R_BRACK "]" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Trait" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rs new file mode 100644 index 000000000000..e1da92060987 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/maybe_const_trait_bound.rs @@ -0,0 +1 @@ +const fn foo(_: impl [const] Trait) {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast index 30a2842e5388..6afa0613f399 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/no_dyn_trait_leading_for.rast @@ -11,13 +11,14 @@ SOURCE_FILE TYPE_BOUND_LIST TYPE_BOUND FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast index 56e2d1095d29..cb296153c8f1 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/question_for_type_trait_bound.rast @@ -29,10 +29,11 @@ SOURCE_FILE TYPE_BOUND QUESTION "?" FOR_TYPE - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast index 0cc365efbe66..b10b953f2fbc 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/where_pred_for.rast @@ -18,13 +18,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0032_where_for.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0032_where_for.rast index 86f6af97c735..dcaf58f7f98e 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0032_where_for.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0032_where_for.rast @@ -36,7 +36,7 @@ SOURCE_FILE PLUS "+" WHITESPACE " " TYPE_BOUND - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" @@ -44,18 +44,18 @@ SOURCE_FILE LIFETIME LIFETIME_IDENT "'de" R_ANGLE ">" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - IDENT "Deserialize" - GENERIC_ARG_LIST - L_ANGLE "<" - LIFETIME_ARG - LIFETIME - LIFETIME_IDENT "'de" - R_ANGLE ">" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "Deserialize" + GENERIC_ARG_LIST + L_ANGLE "<" + LIFETIME_ARG + LIFETIME + LIFETIME_IDENT "'de" + R_ANGLE ">" WHITESPACE " " PLUS "+" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast index 8bf1090f9cfc..5cef4dff062a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0067_where_for_pred.rast @@ -18,13 +18,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH @@ -81,13 +82,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " REF_TYPE AMP "&" @@ -135,13 +137,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PAREN_TYPE L_PAREN "(" @@ -206,13 +209,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " SLICE_TYPE L_BRACK "[" @@ -276,13 +280,14 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'a" + R_ANGLE ">" WHITESPACE " " PATH_TYPE PATH @@ -349,22 +354,24 @@ SOURCE_FILE WHERE_KW "where" WHITESPACE "\n " WHERE_PRED - FOR_KW "for" - GENERIC_PARAM_LIST - L_ANGLE "<" - LIFETIME_PARAM - LIFETIME - LIFETIME_IDENT "'a" - R_ANGLE ">" - WHITESPACE " " - FOR_TYPE + FOR_BINDER FOR_KW "for" GENERIC_PARAM_LIST L_ANGLE "<" LIFETIME_PARAM LIFETIME - LIFETIME_IDENT "'b" + LIFETIME_IDENT "'a" R_ANGLE ">" + WHITESPACE " " + FOR_TYPE + FOR_BINDER + FOR_KW "for" + GENERIC_PARAM_LIST + L_ANGLE "<" + LIFETIME_PARAM + LIFETIME + LIFETIME_IDENT "'b" + R_ANGLE ">" WHITESPACE " " FN_PTR_TYPE FN_KW "fn" diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 4cbc88cfb5ec..6d8a360d715b 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -101,7 +101,7 @@ WhereClause = 'where' predicates:(WherePred (',' WherePred)* ','?) WherePred = - ('for' GenericParamList)? (Lifetime | Type) ':' TypeBoundList? + ForBinder? (Lifetime | Type) ':' TypeBoundList? //*************************// @@ -534,10 +534,10 @@ FieldExpr = Attr* Expr '.' NameRef ClosureExpr = - Attr* ClosureBinder? 'const'? 'static'? 'async'? 'gen'? 'move'? ParamList RetType? + Attr* ForBinder? 'const'? 'static'? 'async'? 'gen'? 'move'? ParamList RetType? body:Expr -ClosureBinder = +ForBinder = 'for' GenericParamList IfExpr = @@ -658,7 +658,7 @@ FnPtrType = 'const'? 'async'? 'unsafe'? Abi? 'fn' ParamList RetType? ForType = - 'for' GenericParamList Type + ForBinder Type ImplTraitType = 'impl' TypeBoundList @@ -671,7 +671,7 @@ TypeBoundList = TypeBound = Lifetime -| ('~' 'const' | '[' 'const' ']' | 'const')? 'async'? '?'? Type +| ForBinder? ('~' 'const' | '[' 'const' ']' | 'const')? 'async'? '?'? Type | 'use' UseBoundGenericArgs UseBoundGenericArgs = diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index d787fd076fc9..a9aeeedb6542 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -393,8 +393,7 @@ where let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); - assert!(pred.for_token().is_none()); - assert!(pred.generic_param_list().is_none()); + assert!(pred.for_binder().is_none()); assert_eq!("T", pred.ty().unwrap().syntax().text().to_string()); assert_bound("Clone", bounds.next()); assert_bound("Copy", bounds.next()); @@ -432,8 +431,10 @@ where let pred = predicates.next().unwrap(); let mut bounds = pred.type_bound_list().unwrap().bounds(); - assert!(pred.for_token().is_some()); - assert_eq!("<'a>", pred.generic_param_list().unwrap().syntax().text().to_string()); + assert_eq!( + "<'a>", + pred.for_binder().unwrap().generic_param_list().unwrap().syntax().text().to_string() + ); assert_eq!("F", pred.ty().unwrap().syntax().text().to_string()); assert_bound("Fn(&'a str)", bounds.next()); } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 2b862465420b..ceb2866ebcdf 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -377,22 +377,13 @@ impl CastExpr { #[inline] pub fn as_token(&self) -> Option { support::token(&self.syntax, T![as]) } } -pub struct ClosureBinder { - pub(crate) syntax: SyntaxNode, -} -impl ClosureBinder { - #[inline] - pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) } - #[inline] - pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } -} pub struct ClosureExpr { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for ClosureExpr {} impl ClosureExpr { #[inline] - pub fn closure_binder(&self) -> Option { support::child(&self.syntax) } + pub fn for_binder(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn param_list(&self) -> Option { support::child(&self.syntax) } #[inline] @@ -615,6 +606,15 @@ impl FnPtrType { #[inline] pub fn unsafe_token(&self) -> Option { support::token(&self.syntax, T![unsafe]) } } +pub struct ForBinder { + pub(crate) syntax: SyntaxNode, +} +impl ForBinder { + #[inline] + pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } +} pub struct ForExpr { pub(crate) syntax: SyntaxNode, } @@ -632,11 +632,9 @@ pub struct ForType { } impl ForType { #[inline] - pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) } + pub fn for_binder(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } - #[inline] - pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } } pub struct FormatArgsArg { pub(crate) syntax: SyntaxNode, @@ -1765,6 +1763,8 @@ pub struct TypeBound { pub(crate) syntax: SyntaxNode, } impl TypeBound { + #[inline] + pub fn for_binder(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn lifetime(&self) -> Option { support::child(&self.syntax) } #[inline] @@ -1938,13 +1938,11 @@ pub struct WherePred { impl ast::HasTypeBounds for WherePred {} impl WherePred { #[inline] - pub fn generic_param_list(&self) -> Option { support::child(&self.syntax) } + pub fn for_binder(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn lifetime(&self) -> Option { support::child(&self.syntax) } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) } - #[inline] - pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } } pub struct WhileExpr { pub(crate) syntax: SyntaxNode, @@ -3239,42 +3237,6 @@ impl fmt::Debug for CastExpr { f.debug_struct("CastExpr").field("syntax", &self.syntax).finish() } } -impl AstNode for ClosureBinder { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - CLOSURE_BINDER - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == CLOSURE_BINDER } - #[inline] - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for ClosureBinder { - fn hash(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for ClosureBinder {} -impl PartialEq for ClosureBinder { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for ClosureBinder { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for ClosureBinder { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("ClosureBinder").field("syntax", &self.syntax).finish() - } -} impl AstNode for ClosureExpr { #[inline] fn kind() -> SyntaxKind @@ -3815,6 +3777,42 @@ impl fmt::Debug for FnPtrType { f.debug_struct("FnPtrType").field("syntax", &self.syntax).finish() } } +impl AstNode for ForBinder { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + FOR_BINDER + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == FOR_BINDER } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for ForBinder { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for ForBinder {} +impl PartialEq for ForBinder { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for ForBinder { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for ForBinder { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ForBinder").field("syntax", &self.syntax).finish() + } +} impl AstNode for ForExpr { #[inline] fn kind() -> SyntaxKind @@ -10146,11 +10144,6 @@ impl std::fmt::Display for CastExpr { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for ClosureBinder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for ClosureExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10226,6 +10219,11 @@ impl std::fmt::Display for FnPtrType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for ForBinder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for ForExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index f5530c5fffd2..62a7d4df2cf6 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -805,9 +805,7 @@ impl ast::SelfParam { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum TypeBoundKind { /// Trait - PathType(ast::PathType), - /// for<'a> ... - ForType(ast::ForType), + PathType(Option, ast::PathType), /// use Use(ast::UseBoundGenericArgs), /// 'a @@ -817,9 +815,7 @@ pub enum TypeBoundKind { impl ast::TypeBound { pub fn kind(&self) -> TypeBoundKind { if let Some(path_type) = support::children(self.syntax()).next() { - TypeBoundKind::PathType(path_type) - } else if let Some(for_type) = support::children(self.syntax()).next() { - TypeBoundKind::ForType(for_type) + TypeBoundKind::PathType(self.for_binder(), path_type) } else if let Some(args) = self.use_bound_generic_args() { TypeBoundKind::Use(args) } else if let Some(lifetime) = self.lifetime() { From 64daba7f1834f0238343b4a53ac502658576fa46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 14:44:50 +0200 Subject: [PATCH 018/201] Remove pull/push functionality from `xtask` --- src/tools/rust-analyzer/Cargo.lock | 11 -- src/tools/rust-analyzer/xtask/Cargo.toml | 1 - src/tools/rust-analyzer/xtask/src/flags.rs | 28 --- src/tools/rust-analyzer/xtask/src/main.rs | 2 - src/tools/rust-analyzer/xtask/src/release.rs | 175 ------------------- 5 files changed, 217 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index c471234bbe3a..a699463ca491 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -395,15 +395,6 @@ dependencies = [ "syn", ] -[[package]] -name = "directories" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs" version = "6.0.0" @@ -3105,7 +3096,6 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", - "directories", "edition", "either", "flate2", @@ -3113,7 +3103,6 @@ dependencies = [ "proc-macro2", "quote", "stdx", - "time", "ungrammar", "write-json", "xflags", diff --git a/src/tools/rust-analyzer/xtask/Cargo.toml b/src/tools/rust-analyzer/xtask/Cargo.toml index 8cd5811c0a6d..9d8a1956d0ad 100644 --- a/src/tools/rust-analyzer/xtask/Cargo.toml +++ b/src/tools/rust-analyzer/xtask/Cargo.toml @@ -8,7 +8,6 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true -directories = "6.0" flate2 = "1.1.2" write-json = "0.1.4" xshell.workspace = true diff --git a/src/tools/rust-analyzer/xtask/src/flags.rs b/src/tools/rust-analyzer/xtask/src/flags.rs index 2fd471b35c7e..72f6215d4c3f 100644 --- a/src/tools/rust-analyzer/xtask/src/flags.rs +++ b/src/tools/rust-analyzer/xtask/src/flags.rs @@ -59,20 +59,6 @@ xflags::xflags! { optional --dry-run } - cmd rustc-pull { - /// rustc commit to pull. - optional --commit refspec: String - } - - cmd rustc-push { - /// rust local path, e.g. `../rust-rust-analyzer`. - required --rust-path rust_path: String - /// rust fork name, e.g. `matklad/rust`. - required --rust-fork rust_fork: String - /// branch name. - optional --branch branch: String - } - cmd dist { /// Use mimalloc allocator for server optional --mimalloc @@ -121,8 +107,6 @@ pub enum XtaskCmd { Install(Install), FuzzTests(FuzzTests), Release(Release), - RustcPull(RustcPull), - RustcPush(RustcPush), Dist(Dist), PublishReleaseNotes(PublishReleaseNotes), Metrics(Metrics), @@ -151,18 +135,6 @@ pub struct Release { pub dry_run: bool, } -#[derive(Debug)] -pub struct RustcPull { - pub commit: Option, -} - -#[derive(Debug)] -pub struct RustcPush { - pub rust_path: String, - pub rust_fork: String, - pub branch: Option, -} - #[derive(Debug)] pub struct Dist { pub mimalloc: bool, diff --git a/src/tools/rust-analyzer/xtask/src/main.rs b/src/tools/rust-analyzer/xtask/src/main.rs index aaa8d0e1d4d4..c5ad49cdcea9 100644 --- a/src/tools/rust-analyzer/xtask/src/main.rs +++ b/src/tools/rust-analyzer/xtask/src/main.rs @@ -42,8 +42,6 @@ fn main() -> anyhow::Result<()> { flags::XtaskCmd::Install(cmd) => cmd.run(sh), flags::XtaskCmd::FuzzTests(_) => run_fuzzer(sh), flags::XtaskCmd::Release(cmd) => cmd.run(sh), - flags::XtaskCmd::RustcPull(cmd) => cmd.run(sh), - flags::XtaskCmd::RustcPush(cmd) => cmd.run(sh), flags::XtaskCmd::Dist(cmd) => cmd.run(sh), flags::XtaskCmd::PublishReleaseNotes(cmd) => cmd.run(sh), flags::XtaskCmd::Metrics(cmd) => cmd.run(sh), diff --git a/src/tools/rust-analyzer/xtask/src/release.rs b/src/tools/rust-analyzer/xtask/src/release.rs index e41f4ceb4350..d06a25c8929b 100644 --- a/src/tools/rust-analyzer/xtask/src/release.rs +++ b/src/tools/rust-analyzer/xtask/src/release.rs @@ -1,12 +1,5 @@ mod changelog; -use std::process::{Command, Stdio}; -use std::thread; -use std::time::Duration; - -use anyhow::{Context as _, bail}; -use directories::ProjectDirs; -use stdx::JodChild; use xshell::{Shell, cmd}; use crate::{date_iso, flags, is_release_tag, project_root}; @@ -59,171 +52,3 @@ impl flags::Release { Ok(()) } } - -// git sync implementation adapted from https://github.com/rust-lang/miri/blob/62039ac/miri-script/src/commands.rs -impl flags::RustcPull { - pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { - sh.change_dir(project_root()); - let commit = self.commit.map(Result::Ok).unwrap_or_else(|| { - let rust_repo_head = - cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?; - rust_repo_head - .split_whitespace() - .next() - .map(|front| front.trim().to_owned()) - .ok_or_else(|| anyhow::format_err!("Could not obtain Rust repo HEAD from remote.")) - })?; - // Make sure the repo is clean. - if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { - bail!("working directory must be clean before running `cargo xtask pull`"); - } - // This should not add any new root commits. So count those before and after merging. - let num_roots = || -> anyhow::Result { - Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") - .read() - .context("failed to determine the number of root commits")? - .parse::()?) - }; - let num_roots_before = num_roots()?; - // Make sure josh is running. - let josh = start_josh()?; - - // Update rust-version file. As a separate commit, since making it part of - // the merge has confused the heck out of josh in the past. - // We pass `--no-verify` to avoid running any git hooks that might exist, - // in case they dirty the repository. - sh.write_file("rust-version", format!("{commit}\n"))?; - const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rust-lang/rust"; - cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") - .run() - .context("FAILED to commit rust-version file, something went wrong")?; - - // Fetch given rustc commit. - cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git") - .run() - .inspect_err(|_| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it it. - cmd!(sh, "git reset --hard HEAD^") - .run() - .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); - }) - .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; - - // Merge the fetched commit. - const MERGE_COMMIT_MESSAGE: &str = "Merge from rust-lang/rust"; - cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") - .run() - .context("FAILED to merge new commits, something went wrong")?; - - // Check that the number of roots did not increase. - if num_roots()? != num_roots_before { - bail!("Josh created a new root commit. This is probably not the history you want."); - } - - drop(josh); - Ok(()) - } -} - -impl flags::RustcPush { - pub(crate) fn run(self, sh: &Shell) -> anyhow::Result<()> { - let branch = self.branch.as_deref().unwrap_or("sync-from-ra"); - let rust_path = self.rust_path; - let rust_fork = self.rust_fork; - - sh.change_dir(project_root()); - let base = sh.read_file("rust-version")?.trim().to_owned(); - // Make sure the repo is clean. - if !cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty() { - bail!("working directory must be clean before running `cargo xtask push`"); - } - // Make sure josh is running. - let josh = start_josh()?; - - // Find a repo we can do our preparation in. - sh.change_dir(rust_path); - - // Prepare the branch. Pushing works much better if we use as base exactly - // the commit that we pulled from last time, so we use the `rust-version` - // file to find out which commit that would be. - println!("Preparing {rust_fork} (base: {base})..."); - if cmd!(sh, "git fetch https://github.com/{rust_fork} {branch}") - .ignore_stderr() - .read() - .is_ok() - { - bail!( - "The branch `{branch}` seems to already exist in `https://github.com/{rust_fork}`. Please delete it and try again." - ); - } - cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; - cmd!(sh, "git push https://github.com/{rust_fork} {base}:refs/heads/{branch}") - .ignore_stdout() - .ignore_stderr() // silence the "create GitHub PR" message - .run()?; - println!(); - - // Do the actual push. - sh.change_dir(project_root()); - println!("Pushing rust-analyzer changes..."); - cmd!( - sh, - "git push http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git HEAD:{branch}" - ) - .run()?; - println!(); - - // Do a round-trip check to make sure the push worked as expected. - cmd!( - sh, - "git fetch http://localhost:{JOSH_PORT}/{rust_fork}.git{JOSH_FILTER}.git {branch}" - ) - .ignore_stderr() - .read()?; - let head = cmd!(sh, "git rev-parse HEAD").read()?; - let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; - if head != fetch_head { - bail!( - "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ - Expected {head}, got {fetch_head}." - ); - } - println!( - "Confirmed that the push round-trips back to rust-analyzer properly. Please create a rustc PR:" - ); - // https://github.com/github-linguist/linguist/compare/master...octocat:linguist:master - let fork_path = rust_fork.replace('/', ":"); - println!( - " https://github.com/rust-lang/rust/compare/{fork_path}:{branch}?quick_pull=1&title=Subtree+update+of+rust-analyzer&body=r?+@ghost" - ); - - drop(josh); - Ok(()) - } -} - -/// Used for rustc syncs. -const JOSH_FILTER: &str = ":rev(55d9a533b309119c8acd13061581b43ae8840823:prefix=src/tools/rust-analyzer):/src/tools/rust-analyzer"; -const JOSH_PORT: &str = "42042"; - -fn start_josh() -> anyhow::Result { - // Determine cache directory. - let local_dir = { - let user_dirs = ProjectDirs::from("org", "rust-lang", "rust-analyzer-josh").unwrap(); - user_dirs.cache_dir().to_owned() - }; - - // Start josh, silencing its output. - let mut cmd = Command::new("josh-proxy"); - cmd.arg("--local").arg(local_dir); - cmd.arg("--remote").arg("https://github.com"); - cmd.arg("--port").arg(JOSH_PORT); - cmd.arg("--no-background"); - cmd.stdout(Stdio::null()); - cmd.stderr(Stdio::null()); - let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; - // Give it some time so hopefully the port is open. (100ms was not enough.) - thread::sleep(Duration::from_millis(200)); - - Ok(JodChild(josh)) -} From 75a1a04a352d35aa584bef2bc1a9b4fd591f3cca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 15:21:58 +0200 Subject: [PATCH 019/201] Document synces using `josh-sync` --- .../docs/book/src/contributing/README.md | 53 ++++++++++++++----- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index beb94cdfc416..80c3e1256609 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -252,18 +252,8 @@ Release steps: 4. Commit & push the changelog. 5. Run `cargo xtask publish-release-notes ` -- this will convert the changelog entry in AsciiDoc to Markdown and update the body of GitHub Releases entry. 6. Tweet. -7. Make a new branch and run `cargo xtask rustc-pull`, open a PR, and merge it. - This will pull any changes from `rust-lang/rust` into `rust-analyzer`. -8. Switch to `master`, pull, then run `cargo xtask rustc-push --rust-path ../rust-rust-analyzer --rust-fork matklad/rust`. - Replace `matklad/rust` with your own fork of `rust-lang/rust`. - You can use the token to authenticate when you get prompted for a password, since `josh` will push over HTTPS, not SSH. - This will push the `rust-analyzer` changes to your fork. - You can then open a PR against `rust-lang/rust`. - -Note: besides the `rust-rust-analyzer` clone, the Josh cache (stored under `~/.cache/rust-analyzer-josh`) will contain a bare clone of `rust-lang/rust`. -This currently takes about 3.5 GB. - -This [HackMD](https://hackmd.io/7pOuxnkdQDaL1Y1FQr65xg) has details about how `josh` syncs work. +7. Perform a subtree [pull](#performing-a-pull). +8. Perform a subtree [push](#performing-a-push). If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. If it fails because of something that needs to be fixed, remove the release tag (if needed), fix the problem, then start over. @@ -288,3 +278,42 @@ There are two sets of people with extra permissions: If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! * The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). This team has general triaging permissions allowing to label, close and re-open issues. + +## Synchronizing subtree changes +`rust-analyzer` is a [josh](https://josh-project.github.io/josh/intro.html) subtree of the [rust-lang/rust](https://github.com/rust-lang/rust) +repository. We use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization between these two +repositories. You can find documentation of the tool [here](https://github.com/rust-lang/josh-sync?tab=readme-ov-file#performing-pull). + +You can install the synchronization tool using the following commands: +``` +cargo install --locked --git https://github.com/rust-lang/josh-sync +``` + +Both pulls (synchronize changes from rust-lang/rust into rust-analyzer) and pushes (synchronize +changes from rust-analyzer to rust-lang/rust) are performed from this repository. + +Usually we first perform a pull, wait for it to be merged, and then perform a push. + +### Performing a pull +1) Checkout a new branch that will be used to create a PR into rust-analyzer +2) Run the pull command + ``` + rustc-josh-sync pull + ``` +3) Push the branch to your fork of `rust-analyzer` and create a PR + - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you. + +### Performing a push + +Wait for the previous pull to be merged. + +1) Switch to `master` and pull +2) Run the push command to create a branch named `` in a `rustc` fork under the `` account + ``` + rustc-josh-sync push + ``` + - The push will ask you to download a checkout of the `rust-lang/rust` repository. + - If you get prompted for a password, see [this](https://github.com/rust-lang/josh-sync?tab=readme-ov-file#git-peculiarities). +3) Create a PR from `` into `rust-lang/rust` + +> Besides the `rust` checkout, the Josh cache (stored under `~/.cache/rustc-josh`) will contain a bare clone of `rust-lang/rust`. This currently takes several GBs. From 0494311b9c09fc72a17d040299eebdb189e582d2 Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 22 Jul 2025 11:45:05 +0000 Subject: [PATCH 020/201] miropt: clippy fixes --- src/tools/miropt-test-tools/src/lib.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/tools/miropt-test-tools/src/lib.rs b/src/tools/miropt-test-tools/src/lib.rs index 41b53d2ad0e9..10769c9c8abf 100644 --- a/src/tools/miropt-test-tools/src/lib.rs +++ b/src/tools/miropt-test-tools/src/lib.rs @@ -34,7 +34,7 @@ fn output_file_suffix(testfile: &Path, bit_width: u32, panic_strategy: PanicStra let mut suffix = String::new(); if each_bit_width { - suffix.push_str(&format!(".{}bit", bit_width)); + suffix.push_str(&format!(".{bit_width}bit")); } if each_panic_strategy { match panic_strategy { @@ -51,7 +51,7 @@ pub fn files_for_miropt_test( panic_strategy: PanicStrategy, ) -> MiroptTest { let mut out = Vec::new(); - let test_file_contents = fs::read_to_string(&testfile).unwrap(); + let test_file_contents = fs::read_to_string(testfile).unwrap(); let test_dir = testfile.parent().unwrap(); let test_crate = testfile.file_stem().unwrap().to_str().unwrap().replace('-', "_"); @@ -76,10 +76,10 @@ pub fn files_for_miropt_test( if test_name.ends_with(".diff") { let trimmed = test_name.trim_end_matches(".diff"); - passes.push(trimmed.split('.').last().unwrap().to_owned()); - let test_against = format!("{}.after.mir", trimmed); - from_file = format!("{}.before.mir", trimmed); - expected_file = format!("{}{}.diff", trimmed, suffix); + passes.push(trimmed.split('.').next_back().unwrap().to_owned()); + let test_against = format!("{trimmed}.after.mir"); + from_file = format!("{trimmed}.before.mir"); + expected_file = format!("{trimmed}{suffix}.diff"); assert!(test_names.next().is_none(), "two mir pass names specified for MIR diff"); to_file = Some(test_against); } else if let Some(first_pass) = test_names.next() { @@ -92,10 +92,9 @@ pub fn files_for_miropt_test( } assert!(test_names.next().is_none(), "three mir pass names specified for MIR diff"); - expected_file = - format!("{}{}.{}-{}.diff", test_name, suffix, first_pass, second_pass); - let second_file = format!("{}.{}.mir", test_name, second_pass); - from_file = format!("{}.{}.mir", test_name, first_pass); + expected_file = format!("{test_name}{suffix}.{first_pass}-{second_pass}.diff"); + let second_file = format!("{test_name}.{second_pass}.mir"); + from_file = format!("{test_name}.{first_pass}.mir"); to_file = Some(second_file); } else { // Allow-list for file extensions that can be produced by MIR dumps. @@ -112,7 +111,7 @@ pub fn files_for_miropt_test( ) } - expected_file = format!("{}{}.{}", test_name_wo_ext, suffix, test_name_ext); + expected_file = format!("{test_name_wo_ext}{suffix}.{test_name_ext}"); from_file = test_name.to_string(); assert!(test_names.next().is_none(), "two mir pass names specified for MIR dump"); to_file = None; @@ -123,7 +122,7 @@ pub fn files_for_miropt_test( ); }; if !expected_file.starts_with(&test_crate) { - expected_file = format!("{}.{}", test_crate, expected_file); + expected_file = format!("{test_crate}.{expected_file}"); } let expected_file = test_dir.join(expected_file); From 39ef6a96ad6d4d3340a3597e1efe9755d52c690b Mon Sep 17 00:00:00 2001 From: Marijn Schouten Date: Tue, 22 Jul 2025 11:45:38 +0000 Subject: [PATCH 021/201] miropt: move to edition 2024 --- src/tools/miropt-test-tools/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miropt-test-tools/Cargo.toml b/src/tools/miropt-test-tools/Cargo.toml index 09b4c7d16dce..3eb5020968d3 100644 --- a/src/tools/miropt-test-tools/Cargo.toml +++ b/src/tools/miropt-test-tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "miropt-test-tools" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] From 0c22e6123be4b40947be3aa25e2e8c0dbe4d51cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 18:14:20 +0200 Subject: [PATCH 022/201] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- .../rust-analyzer/docs/book/src/contributing/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/README.md b/src/tools/rust-analyzer/docs/book/src/contributing/README.md index 80c3e1256609..57c7a9c5996d 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/README.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/README.md @@ -282,26 +282,27 @@ There are two sets of people with extra permissions: ## Synchronizing subtree changes `rust-analyzer` is a [josh](https://josh-project.github.io/josh/intro.html) subtree of the [rust-lang/rust](https://github.com/rust-lang/rust) repository. We use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync) tool to perform synchronization between these two -repositories. You can find documentation of the tool [here](https://github.com/rust-lang/josh-sync?tab=readme-ov-file#performing-pull). +repositories. You can find documentation of the tool [here](https://github.com/rust-lang/josh-sync). You can install the synchronization tool using the following commands: ``` cargo install --locked --git https://github.com/rust-lang/josh-sync ``` -Both pulls (synchronize changes from rust-lang/rust into rust-analyzer) and pushes (synchronize +Both pulls (synchronizing changes from rust-lang/rust into rust-analyzer) and pushes (synchronizing +changes from rust-analyzer into rust-lang/rust) are performed from this repository. changes from rust-analyzer to rust-lang/rust) are performed from this repository. Usually we first perform a pull, wait for it to be merged, and then perform a push. ### Performing a pull -1) Checkout a new branch that will be used to create a PR into rust-analyzer +1) Checkout a new branch that will be used to create a PR against rust-analyzer 2) Run the pull command ``` rustc-josh-sync pull ``` 3) Push the branch to your fork of `rust-analyzer` and create a PR - - If you have `gh` CLI installed, `rustc-josh-sync` can create the PR for you. + - If you have the `gh` CLI installed, `rustc-josh-sync` can create the PR for you. ### Performing a push From b14869d11ff9b2fef9a99adab8f29b5e7245420f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 22 Jul 2025 18:15:54 +0200 Subject: [PATCH 023/201] Add CI workflow for periodically performing josh pulls --- .../.github/workflows/rustc-pull.yml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/tools/rust-analyzer/.github/workflows/rustc-pull.yml diff --git a/src/tools/rust-analyzer/.github/workflows/rustc-pull.yml b/src/tools/rust-analyzer/.github/workflows/rustc-pull.yml new file mode 100644 index 000000000000..2a842f3b3114 --- /dev/null +++ b/src/tools/rust-analyzer/.github/workflows/rustc-pull.yml @@ -0,0 +1,20 @@ +name: rustc-pull + +on: + workflow_dispatch: + schedule: + # Run at 04:00 UTC every Monday and Thursday + - cron: '0 4 * * 1,4' + +jobs: + pull: + if: github.repository == 'rust-lang/rust-analyzer' + uses: rust-lang/josh-sync/.github/workflows/rustc-pull.yml@main + with: + zulip-stream-id: 185405 + zulip-bot-email: "rust-analyzer-ci-bot@rust-lang.zulipchat.com" + pr-base-branch: master + branch-name: rustc-pull + secrets: + zulip-api-token: ${{ secrets.ZULIP_API_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} From 1023edc49cef9fee8bd8f6d40de67f76feb367c6 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 23 Jul 2025 13:40:38 +0800 Subject: [PATCH 024/201] Change rename self to parameter use `Self` type And add `&self` lifetime support Example === Rename to `this` ```rust struct Foo(T); impl Foo { fn foo(&'static self$0) {} } ``` Old: ```rust struct Foo(T); impl Foo { fn foo(this: &Foo) {} } ``` Fixes: ```rust struct Foo(T); impl Foo { fn foo(this: &'static Self) {} } ``` --- .../rust-analyzer/crates/ide/src/rename.rs | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index fb84e8e6b474..67560fdecb16 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -12,6 +12,7 @@ use ide_db::{ source_change::SourceChangeBuilder, }; use itertools::Itertools; +use std::fmt::Write; use stdx::{always, never}; use syntax::{AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, ast}; @@ -459,35 +460,27 @@ fn rename_self_to_param( } fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> Option { - fn target_type_name(impl_def: &ast::Impl) -> Option { - if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { - return Some(p.path()?.segment()?.name_ref()?.text().to_string()); - } - None + let mut replacement_text = new_name; + replacement_text.push_str(": "); + + if self_param.amp_token().is_some() { + replacement_text.push('&'); + } + if let Some(lifetime) = self_param.lifetime() { + write!(replacement_text, "{lifetime} ").unwrap(); + } + if self_param.amp_token().and(self_param.mut_token()).is_some() { + replacement_text.push_str("mut "); } - match self_param.syntax().ancestors().find_map(ast::Impl::cast) { - Some(impl_def) => { - let type_name = target_type_name(&impl_def)?; - - let mut replacement_text = new_name; - replacement_text.push_str(": "); - match (self_param.amp_token(), self_param.mut_token()) { - (Some(_), None) => replacement_text.push('&'), - (Some(_), Some(_)) => replacement_text.push_str("&mut "), - (_, _) => (), - }; - replacement_text.push_str(type_name.as_str()); - - Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) - } - None => { - cov_mark::hit!(rename_self_outside_of_methods); - let mut replacement_text = new_name; - replacement_text.push_str(": _"); - Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) - } + if self_param.syntax().ancestors().find_map(ast::Impl::cast).is_some() { + replacement_text.push_str("Self"); + } else { + cov_mark::hit!(rename_self_outside_of_methods); + replacement_text.push('_'); } + + Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) } #[cfg(test)] @@ -2069,7 +2062,7 @@ impl Foo { struct Foo { i: i32 } impl Foo { - fn f(foo: &mut Foo) -> i32 { + fn f(foo: &mut Self) -> i32 { foo.i } } @@ -2095,7 +2088,33 @@ impl Foo { struct Foo { i: i32 } impl Foo { - fn f(foo: Foo) -> i32 { + fn f(foo: Self) -> i32 { + foo.i + } +} +"#, + ); + } + + #[test] + fn test_owned_self_to_parameter_with_lifetime() { + cov_mark::check!(rename_self_to_param); + check( + "foo", + r#" +struct Foo<'a> { i: &'a i32 } + +impl<'a> Foo<'a> { + fn f(&'a $0self) -> i32 { + self.i + } +} +"#, + r#" +struct Foo<'a> { i: &'a i32 } + +impl<'a> Foo<'a> { + fn f(foo: &'a Self) -> i32 { foo.i } } @@ -2159,7 +2178,7 @@ impl Foo { struct Foo { i: i32 } impl Foo { - fn f(foo: &Foo) -> i32 { + fn f(foo: &Self) -> i32 { let self_var = 1; foo.i } From 08d77873df5670e875c5ca18a6717a56436687a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 19 Jul 2025 09:45:56 +0200 Subject: [PATCH 025/201] CI: add windows-arm runner --- src/tools/miri/.github/workflows/ci.yml | 8 ++++++++ src/tools/miri/cargo-miri/src/phases.rs | 8 ++++---- src/tools/miri/ci/ci.sh | 12 ++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 11c0f08debe6..1af443803910 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -45,6 +45,8 @@ jobs: os: macos-latest - host_target: i686-pc-windows-msvc os: windows-latest + - host_target: aarch64-pc-windows-msvc + os: windows-11-arm runs-on: ${{ matrix.os }} env: HOST_TARGET: ${{ matrix.host_target }} @@ -63,6 +65,12 @@ jobs: sudo apt update # Install needed packages sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g') + - name: Install rustup on Windows ARM + if: ${{ matrix.os == 'windows-11-arm' }} + run: | + curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe + ./rustup-init.exe -y --no-modify-path + echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH" - uses: ./.github/workflows/setup with: toolchain_flags: "--host ${{ matrix.host_target }}" diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index b72b974bdbdd..efb9053f69a5 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -1,9 +1,9 @@ //! Implements the various phases of `cargo miri run/test`. use std::env; -use std::fs::{self, File}; +use std::fs::File; use std::io::BufReader; -use std::path::{Path, PathBuf}; +use std::path::{self, Path, PathBuf}; use std::process::Command; use rustc_version::VersionMeta; @@ -222,12 +222,12 @@ pub fn phase_cargo_miri(mut args: impl Iterator) { // that to be the Miri driver, but acting as rustc, in host mode. // // In `main`, we need the value of `RUSTC` to distinguish RUSTC_WRAPPER invocations from rustdoc - // or TARGET_RUNNER invocations, so we canonicalize it here to make it exceedingly unlikely that + // or TARGET_RUNNER invocations, so we make it absolute to make it exceedingly unlikely that // there would be a collision with other invocations of cargo-miri (as rustdoc or as runner). We // explicitly do this even if RUSTC_STAGE is set, since for these builds we do *not* want the // bootstrap `rustc` thing in our way! Instead, we have MIRI_HOST_SYSROOT to use for host // builds. - cmd.env("RUSTC", fs::canonicalize(find_miri()).unwrap()); + cmd.env("RUSTC", path::absolute(find_miri()).unwrap()); // In case we get invoked as RUSTC without the wrapper, let's be a host rustc. This makes no // sense for cross-interpretation situations, but without the wrapper, this will use the host // sysroot, so asking it to behave like a target build makes even less sense. diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 5767d1782795..b66530e77b8a 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -142,7 +142,6 @@ case $HOST_TARGET in # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests # Extra tier 1 - MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests MANY_SEEDS=64 TEST_TARGET=x86_64-apple-darwin run_tests MANY_SEEDS=64 TEST_TARGET=x86_64-pc-windows-gnu run_tests ;; @@ -161,8 +160,6 @@ case $HOST_TARGET in aarch64-unknown-linux-gnu) # Host GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests - # Extra tier 1 candidate - MANY_SEEDS=64 TEST_TARGET=aarch64-pc-windows-msvc run_tests # Extra tier 2 MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI @@ -189,13 +186,20 @@ case $HOST_TARGET in ;; i686-pc-windows-msvc) # Host - # Without GC_STRESS as this is the slowest runner. + # Without GC_STRESS as this is a very slow runner. MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests # Extra tier 1 # We really want to ensure a Linux target works on a Windows host, # and a 64bit target works on a 32bit host. TEST_TARGET=x86_64-unknown-linux-gnu run_tests ;; + aarch64-pc-windows-msvc) + # Host + # Without GC_STRESS as this is a very slow runner. + MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests + # Extra tier 1 + MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests + ;; *) echo "FATAL: unknown host target: $HOST_TARGET" exit 1 From a6c10939acdadf274e67f97f1e605b4e563a8c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 23 Jul 2025 09:24:52 +0200 Subject: [PATCH 026/201] Init josh-sync config file --- src/tools/miri/josh-sync.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/tools/miri/josh-sync.toml diff --git a/src/tools/miri/josh-sync.toml b/src/tools/miri/josh-sync.toml new file mode 100644 index 000000000000..86208b3742d2 --- /dev/null +++ b/src/tools/miri/josh-sync.toml @@ -0,0 +1,2 @@ +repo = "miri" +filter = ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri" From a61657e9d477914b27529bb2fe6dbb1d76e5ae1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 23 Jul 2025 09:24:58 +0200 Subject: [PATCH 027/201] Remove Josh code from `miri-script` --- src/tools/miri/miri-script/Cargo.lock | 90 +------- src/tools/miri/miri-script/Cargo.toml | 1 - src/tools/miri/miri-script/src/commands.rs | 229 +-------------------- src/tools/miri/miri-script/src/main.rs | 22 +- 4 files changed, 6 insertions(+), 336 deletions(-) diff --git a/src/tools/miri/miri-script/Cargo.lock b/src/tools/miri/miri-script/Cargo.lock index a049bfcbccd6..044a678869e2 100644 --- a/src/tools/miri/miri-script/Cargo.lock +++ b/src/tools/miri/miri-script/Cargo.lock @@ -116,27 +116,6 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" -[[package]] -name = "directories" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.60.2", -] - [[package]] name = "dunce" version = "1.0.5" @@ -165,17 +144,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "getrandom" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.1+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.3.3" @@ -185,7 +153,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi", ] [[package]] @@ -221,16 +189,6 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" -[[package]] -name = "libredox" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" -dependencies = [ - "bitflags", - "libc", -] - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -249,7 +207,6 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "directories", "dunce", "itertools", "path_macro", @@ -275,12 +232,6 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "path_macro" version = "1.0.0" @@ -311,17 +262,6 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" -[[package]] -name = "redox_users" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" -dependencies = [ - "getrandom 0.2.16", - "libredox", - "thiserror", -] - [[package]] name = "rustc_version" version = "0.4.1" @@ -427,32 +367,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", ] -[[package]] -name = "thiserror" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "unicode-ident" version = "1.0.18" @@ -475,12 +395,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - [[package]] name = "wasi" version = "0.14.2+wasi-0.2.4" diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml index b3f82cd1d504..39858880e8c6 100644 --- a/src/tools/miri/miri-script/Cargo.toml +++ b/src/tools/miri/miri-script/Cargo.toml @@ -22,7 +22,6 @@ anyhow = "1.0" xshell = "0.2.6" rustc_version = "0.4" dunce = "1.0.4" -directories = "6" serde = "1" serde_json = "1" serde_derive = "1" diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 9aaad9ca04a9..017e461cd807 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -2,11 +2,9 @@ use std::collections::BTreeMap; use std::ffi::{OsStr, OsString}; use std::fmt::Write as _; use std::fs::{self, File}; -use std::io::{self, BufRead, BufReader, BufWriter, Write as _}; -use std::ops::Not; +use std::io::{self, BufRead, BufReader, BufWriter}; use std::path::PathBuf; -use std::time::Duration; -use std::{env, net, process}; +use std::{env, process}; use anyhow::{Context, Result, anyhow, bail}; use path_macro::path; @@ -18,11 +16,6 @@ use xshell::{Shell, cmd}; use crate::Command; use crate::util::*; -/// Used for rustc syncs. -const JOSH_FILTER: &str = - ":rev(75dd959a3a40eb5b4574f8d2e23aa6efbeb33573:prefix=src/tools/miri):/src/tools/miri"; -const JOSH_PORT: u16 = 42042; - impl MiriEnv { /// Prepares the environment: builds miri and cargo-miri and a sysroot. /// Returns the location of the sysroot. @@ -99,66 +92,6 @@ impl Command { Ok(()) } - fn start_josh() -> Result { - // Determine cache directory. - let local_dir = { - let user_dirs = - directories::ProjectDirs::from("org", "rust-lang", "miri-josh").unwrap(); - user_dirs.cache_dir().to_owned() - }; - - // Start josh, silencing its output. - let mut cmd = process::Command::new("josh-proxy"); - cmd.arg("--local").arg(local_dir); - cmd.arg("--remote").arg("https://github.com"); - cmd.arg("--port").arg(JOSH_PORT.to_string()); - cmd.arg("--no-background"); - cmd.stdout(process::Stdio::null()); - cmd.stderr(process::Stdio::null()); - let josh = cmd.spawn().context("failed to start josh-proxy, make sure it is installed")?; - - // Create a wrapper that stops it on drop. - struct Josh(process::Child); - impl Drop for Josh { - fn drop(&mut self) { - #[cfg(unix)] - { - // Try to gracefully shut it down. - process::Command::new("kill") - .args(["-s", "INT", &self.0.id().to_string()]) - .output() - .expect("failed to SIGINT josh-proxy"); - // Sadly there is no "wait with timeout"... so we just give it some time to finish. - std::thread::sleep(Duration::from_millis(100)); - // Now hopefully it is gone. - if self.0.try_wait().expect("failed to wait for josh-proxy").is_some() { - return; - } - } - // If that didn't work (or we're not on Unix), kill it hard. - eprintln!( - "I have to kill josh-proxy the hard way, let's hope this does not break anything." - ); - self.0.kill().expect("failed to SIGKILL josh-proxy"); - } - } - - // Wait until the port is open. We try every 10ms until 1s passed. - for _ in 0..100 { - // This will generally fail immediately when the port is still closed. - let josh_ready = net::TcpStream::connect_timeout( - &net::SocketAddr::from(([127, 0, 0, 1], JOSH_PORT)), - Duration::from_millis(1), - ); - if josh_ready.is_ok() { - return Ok(Josh(josh)); - } - // Not ready yet. - std::thread::sleep(Duration::from_millis(10)); - } - bail!("Even after waiting for 1s, josh-proxy is still not available.") - } - pub fn exec(self) -> Result<()> { // First, and crucially only once, run the auto-actions -- but not for all commands. match &self { @@ -170,11 +103,7 @@ impl Command { | Command::Fmt { .. } | Command::Doc { .. } | Command::Clippy { .. } => Self::auto_actions()?, - | Command::Toolchain { .. } - | Command::Bench { .. } - | Command::RustcPull { .. } - | Command::RustcPush { .. } - | Command::Squash => {} + | Command::Toolchain { .. } | Command::Bench { .. } | Command::Squash => {} } // Then run the actual command. match self { @@ -191,8 +120,6 @@ impl Command { Command::Bench { target, no_install, save_baseline, load_baseline, benches } => Self::bench(target, no_install, save_baseline, load_baseline, benches), Command::Toolchain { flags } => Self::toolchain(flags), - Command::RustcPull { commit } => Self::rustc_pull(commit.clone()), - Command::RustcPush { github_user, branch } => Self::rustc_push(github_user, branch), Command::Squash => Self::squash(), } } @@ -233,156 +160,6 @@ impl Command { Ok(()) } - fn rustc_pull(commit: Option) -> Result<()> { - let sh = Shell::new()?; - sh.change_dir(miri_dir()?); - let commit = commit.map(Result::Ok).unwrap_or_else(|| { - let rust_repo_head = - cmd!(sh, "git ls-remote https://github.com/rust-lang/rust/ HEAD").read()?; - rust_repo_head - .split_whitespace() - .next() - .map(|front| front.trim().to_owned()) - .ok_or_else(|| anyhow!("Could not obtain Rust repo HEAD from remote.")) - })?; - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - bail!("working directory must be clean before running `./miri rustc-pull`"); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git"); - - // Update rust-version file. As a separate commit, since making it part of - // the merge has confused the heck out of josh in the past. - // We pass `--no-verify` to avoid running git hooks like `./miri fmt` that could in turn - // trigger auto-actions. - // We do this before the merge so that if there are merge conflicts, we have - // the right rust-version file while resolving them. - sh.write_file("rust-version", format!("{commit}\n"))?; - const PREPARING_COMMIT_MESSAGE: &str = "Preparing for merge from rustc"; - cmd!(sh, "git commit rust-version --no-verify -m {PREPARING_COMMIT_MESSAGE}") - .run() - .context("FAILED to commit rust-version file, something went wrong")?; - - // Fetch given rustc commit. - cmd!(sh, "git fetch {josh_url}") - .run() - .inspect_err(|_| { - // Try to un-do the previous `git commit`, to leave the repo in the state we found it. - cmd!(sh, "git reset --hard HEAD^") - .run() - .expect("FAILED to clean up again after failed `git fetch`, sorry for that"); - }) - .context("FAILED to fetch new commits, something went wrong (committing the rust-version file has been undone)")?; - - // This should not add any new root commits. So count those before and after merging. - let num_roots = || -> Result { - Ok(cmd!(sh, "git rev-list HEAD --max-parents=0 --count") - .read() - .context("failed to determine the number of root commits")? - .parse::()?) - }; - let num_roots_before = num_roots()?; - - // Merge the fetched commit. - const MERGE_COMMIT_MESSAGE: &str = "Merge from rustc"; - cmd!(sh, "git merge FETCH_HEAD --no-verify --no-ff -m {MERGE_COMMIT_MESSAGE}") - .run() - .context("FAILED to merge new commits, something went wrong")?; - - // Check that the number of roots did not increase. - if num_roots()? != num_roots_before { - bail!("Josh created a new root commit. This is probably not the history you want."); - } - - drop(josh); - Ok(()) - } - - fn rustc_push(github_user: String, branch: String) -> Result<()> { - let sh = Shell::new()?; - sh.change_dir(miri_dir()?); - let base = sh.read_file("rust-version")?.trim().to_owned(); - // Make sure the repo is clean. - if cmd!(sh, "git status --untracked-files=no --porcelain").read()?.is_empty().not() { - bail!("working directory must be clean before running `./miri rustc-push`"); - } - // Make sure josh is running. - let josh = Self::start_josh()?; - let josh_url = - format!("http://localhost:{JOSH_PORT}/{github_user}/rust.git{JOSH_FILTER}.git"); - - // Find a repo we can do our preparation in. - if let Ok(rustc_git) = env::var("RUSTC_GIT") { - // If rustc_git is `Some`, we'll use an existing fork for the branch updates. - sh.change_dir(rustc_git); - } else { - // Otherwise, do this in the local Miri repo. - println!( - "This will pull a copy of the rust-lang/rust history into this Miri checkout, growing it by about 1GB." - ); - print!( - "To avoid that, abort now and set the `RUSTC_GIT` environment variable to an existing rustc checkout. Proceed? [y/N] " - ); - std::io::stdout().flush()?; - let mut answer = String::new(); - std::io::stdin().read_line(&mut answer)?; - if answer.trim().to_lowercase() != "y" { - std::process::exit(1); - } - }; - // Prepare the branch. Pushing works much better if we use as base exactly - // the commit that we pulled from last time, so we use the `rust-version` - // file to find out which commit that would be. - println!("Preparing {github_user}/rust (base: {base})..."); - if cmd!(sh, "git fetch https://github.com/{github_user}/rust {branch}") - .ignore_stderr() - .read() - .is_ok() - { - println!( - "The branch '{branch}' seems to already exist in 'https://github.com/{github_user}/rust'. Please delete it and try again." - ); - std::process::exit(1); - } - cmd!(sh, "git fetch https://github.com/rust-lang/rust {base}").run()?; - cmd!(sh, "git push https://github.com/{github_user}/rust {base}:refs/heads/{branch}") - .ignore_stdout() - .ignore_stderr() // silence the "create GitHub PR" message - .run()?; - println!(); - - // Do the actual push. - sh.change_dir(miri_dir()?); - println!("Pushing miri changes..."); - cmd!(sh, "git push {josh_url} HEAD:{branch}").run()?; - println!(); - - // Do a round-trip check to make sure the push worked as expected. - cmd!(sh, "git fetch {josh_url} {branch}").ignore_stderr().read()?; - let head = cmd!(sh, "git rev-parse HEAD").read()?; - let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?; - if head != fetch_head { - bail!( - "Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\ - Expected {head}, got {fetch_head}." - ); - } - println!( - "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:" - ); - println!( - // Open PR with `subtree update` title to silence the `no-merges` triagebot check - // See https://github.com/rust-lang/rust/pull/114157 - " https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Miri+subtree+update&body=r?+@ghost" - ); - - drop(josh); - Ok(()) - } - fn squash() -> Result<()> { let sh = Shell::new()?; sh.change_dir(miri_dir()?); diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index e41df662ca28..761ec5979faf 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -142,25 +142,6 @@ pub enum Command { #[arg(trailing_var_arg = true, allow_hyphen_values = true)] flags: Vec, }, - /// Pull and merge Miri changes from the rustc repo. - /// - /// The fetched commit is stored in the `rust-version` file, so the next `./miri toolchain` will - /// install the rustc that just got pulled. - RustcPull { - /// The commit to fetch (default: latest rustc commit). - commit: Option, - }, - /// Push Miri changes back to the rustc repo. - /// - /// This will pull a copy of the rustc history into the Miri repo, unless you set the RUSTC_GIT - /// env var to an existing clone of the rustc repo. - RustcPush { - /// The Github user that owns the rustc fork to which we should push. - github_user: String, - /// The branch to push to. - #[arg(default_value = "miri-sync")] - branch: String, - }, /// Squash the commits of the current feature branch into one. Squash, } @@ -184,8 +165,7 @@ impl Command { flags.extend(remainder); Ok(()) } - Self::Bench { .. } | Self::RustcPull { .. } | Self::RustcPush { .. } | Self::Squash => - bail!("unexpected \"--\" found in arguments"), + Self::Bench { .. } | Self::Squash => bail!("unexpected \"--\" found in arguments"), } } } From f6288ba7b72b9441d92e3ef4912675ea179231aa Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 23 Jul 2025 16:27:07 +0300 Subject: [PATCH 028/201] Remove `ExpressionStoreDiagnostics::MacroError`, instead recreate it from the `MacroCallId` This simplifies the code and also makes us report parse error in macros too. --- .../crates/hir-def/src/expr_store.rs | 3 +- .../crates/hir-def/src/expr_store/lower.rs | 12 +----- src/tools/rust-analyzer/crates/hir/src/lib.rs | 38 ++++--------------- 3 files changed, 9 insertions(+), 44 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index d3dfc05eb29d..5695ab7ed005 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -16,7 +16,7 @@ use std::{ use cfg::{CfgExpr, CfgOptions}; use either::Either; -use hir_expand::{ExpandError, InFile, MacroCallId, mod_path::ModPath, name::Name}; +use hir_expand::{InFile, MacroCallId, mod_path::ModPath, name::Name}; use la_arena::{Arena, ArenaMap}; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -281,7 +281,6 @@ struct FormatTemplate { #[derive(Debug, Eq, PartialEq)] pub enum ExpressionStoreDiagnostics { InactiveCode { node: InFile, cfg: CfgExpr, opts: CfgOptions }, - MacroError { node: InFile, err: ExpandError }, UnresolvedMacroCall { node: InFile, path: ModPath }, UnreachableLabel { node: InFile>, name: Name }, AwaitOutsideOfAsync { node: InFile>, location: String }, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 4e877748ca2a..26e590f453ef 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -1981,13 +1981,7 @@ impl ExprCollector<'_> { return collector(self, None); } }; - if record_diagnostics { - if let Some(err) = res.err { - self.store - .diagnostics - .push(ExpressionStoreDiagnostics::MacroError { node: macro_call_ptr, err }); - } - } + // No need to push macro and parsing errors as they'll be recreated from `macro_calls()`. match res.value { Some((mark, expansion)) => { @@ -1997,10 +1991,6 @@ impl ExprCollector<'_> { self.store.expansions.insert(macro_call_ptr, macro_file); } - if record_diagnostics { - // FIXME: Report parse errors here - } - let id = collector(self, expansion.map(|it| it.tree())); self.expander.exit(mark); id diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 1b2b76999f7f..4ddb04b24f7f 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -1922,10 +1922,6 @@ impl DefWithBody { Module { id: def_map.module_id(DefMap::ROOT) }.diagnostics(db, acc, style_lints); } - source_map - .macro_calls() - .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id, acc)); - expr_store_diagnostics(db, acc, &source_map); let infer = db.infer(self.into()); @@ -2130,9 +2126,9 @@ impl DefWithBody { } } -fn expr_store_diagnostics( - db: &dyn HirDatabase, - acc: &mut Vec>, +fn expr_store_diagnostics<'db>( + db: &'db dyn HirDatabase, + acc: &mut Vec>, source_map: &ExpressionStoreSourceMap, ) { for diag in source_map.diagnostics() { @@ -2140,30 +2136,6 @@ fn expr_store_diagnostics( ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => { InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() } - ExpressionStoreDiagnostics::MacroError { node, err } => { - let RenderedExpandError { message, error, kind } = err.render_to_string(db); - - let editioned_file_id = EditionedFileId::from_span(db, err.span().anchor.file_id); - let precise_location = if editioned_file_id == node.file_id { - Some( - err.span().range - + db.ast_id_map(editioned_file_id.into()) - .get_erased(err.span().anchor.ast_id) - .text_range() - .start(), - ) - } else { - None - }; - MacroError { - node: (node).map(|it| it.into()), - precise_location, - message, - error, - kind, - } - .into() - } ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { macro_call: (*node).map(|ast_ptr| ast_ptr.into()), precise_location: None, @@ -2182,6 +2154,10 @@ fn expr_store_diagnostics( } }); } + + source_map + .macro_calls() + .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id, acc)); } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Function { From 3e9bf8c3b84b1208d9ff3975a9fcc5e3afe709fa Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 23 Jul 2025 22:04:59 +0800 Subject: [PATCH 029/201] Remove rename_self_outside_of_methods --- src/tools/rust-analyzer/crates/ide/src/rename.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 67560fdecb16..a07c647c2cb8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -473,12 +473,7 @@ fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> O replacement_text.push_str("mut "); } - if self_param.syntax().ancestors().find_map(ast::Impl::cast).is_some() { - replacement_text.push_str("Self"); - } else { - cov_mark::hit!(rename_self_outside_of_methods); - replacement_text.push('_'); - } + replacement_text.push_str("Self"); Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) } @@ -2124,7 +2119,6 @@ impl<'a> Foo<'a> { #[test] fn test_self_outside_of_methods() { - cov_mark::check!(rename_self_outside_of_methods); check( "foo", r#" @@ -2133,7 +2127,7 @@ fn f($0self) -> i32 { } "#, r#" -fn f(foo: _) -> i32 { +fn f(foo: Self) -> i32 { foo.i } "#, From b4724b4b6147dc8968fc072b4b3815c1e4f662f4 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Wed, 23 Jul 2025 17:35:08 +0300 Subject: [PATCH 030/201] Fix Cargo.lock The dependency of `xtask` on `time` was mistakenly removed. --- src/tools/rust-analyzer/Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a699463ca491..1e7d449e44a3 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -3103,6 +3103,7 @@ dependencies = [ "proc-macro2", "quote", "stdx", + "time", "ungrammar", "write-json", "xflags", From c60cec3411b38c13bb9b1ea92e02bbc8b5db5984 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Wed, 23 Jul 2025 23:19:59 +0900 Subject: [PATCH 031/201] Use `TempDir` for copied lockfiles --- src/tools/rust-analyzer/Cargo.lock | 8 ++++ src/tools/rust-analyzer/Cargo.toml | 1 + .../crates/project-model/Cargo.toml | 1 + .../project-model/src/cargo_workspace.rs | 48 +++++++++++++++++-- .../crates/project-model/src/workspace.rs | 3 +- 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a699463ca491..c19e8471647f 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1679,6 +1679,7 @@ dependencies = [ "serde_json", "span", "stdx", + "temp-dir", "toolchain", "tracing", "triomphe", @@ -2284,6 +2285,12 @@ dependencies = [ "tt", ] +[[package]] +name = "temp-dir" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" + [[package]] name = "tenthash" version = "1.1.0" @@ -3103,6 +3110,7 @@ dependencies = [ "proc-macro2", "quote", "stdx", + "time", "ungrammar", "write-json", "xflags", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 700c116ec182..87202e86d3f1 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -156,6 +156,7 @@ smallvec = { version = "1.15.1", features = [ "const_generics", ] } smol_str = "0.3.2" +temp-dir = "0.1.16" text-size = "1.1.1" tracing = "0.1.41" tracing-tree = "0.4.0" diff --git a/src/tools/rust-analyzer/crates/project-model/Cargo.toml b/src/tools/rust-analyzer/crates/project-model/Cargo.toml index 27fe9f79bbc5..0dbb309a62a6 100644 --- a/src/tools/rust-analyzer/crates/project-model/Cargo.toml +++ b/src/tools/rust-analyzer/crates/project-model/Cargo.toml @@ -20,6 +20,7 @@ semver.workspace = true serde_json.workspace = true serde.workspace = true serde_derive.workspace = true +temp-dir.workspace = true tracing.workspace = true triomphe.workspace = true la-arena.workspace = true diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index daadcd9d79a9..25abb19d9d55 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -552,6 +552,7 @@ impl CargoWorkspace { pub(crate) struct FetchMetadata { command: cargo_metadata::MetadataCommand, + manifest_path: ManifestPath, lockfile_path: Option, kind: &'static str, no_deps: bool, @@ -655,7 +656,15 @@ impl FetchMetadata { } .with_context(|| format!("Failed to run `{cargo_command:?}`")); - Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options } + Self { + manifest_path: cargo_toml.clone(), + command, + lockfile_path, + kind: config.kind, + no_deps, + no_deps_result, + other_options, + } } pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> { @@ -672,18 +681,47 @@ impl FetchMetadata { locked: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option)> { - let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } = - self; + let Self { + mut command, + manifest_path, + lockfile_path, + kind, + no_deps, + no_deps_result, + mut other_options, + } = self; if no_deps { return no_deps_result.map(|m| (m, None)); } let mut using_lockfile_copy = false; + let mut _temp_dir_guard = None; // The manifest is a rust file, so this means its a script manifest if let Some(lockfile) = lockfile_path { - let target_lockfile = - target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock"); + _temp_dir_guard = temp_dir::TempDir::with_prefix("rust-analyzer").ok(); + let target_lockfile = _temp_dir_guard + .and_then(|tmp| tmp.path().join("Cargo.lock").try_into().ok()) + .unwrap_or_else(|| { + // When multiple workspaces share the same target dir, they might overwrite into a + // single lockfile path. + // See https://github.com/rust-lang/rust-analyzer/issues/20189#issuecomment-3073520255 + let manifest_path_hash = std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &manifest_path, + ); + let disambiguator = format!( + "{}_{manifest_path_hash}", + manifest_path.components().nth_back(1).map_or("", |c| c.as_str()) + ); + + target_dir + .join("rust-analyzer") + .join("metadata") + .join(kind) + .join(disambiguator) + .join("Cargo.lock") + }); match std::fs::copy(&lockfile, &target_lockfile) { Ok(_) => { using_lockfile_copy = true; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 677f29e3c60a..655da118bbd4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1905,7 +1905,8 @@ fn cargo_target_dir( meta.manifest_path(manifest); // `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve. // So we can use it to get `target_directory` before copying lockfiles - let mut other_options = vec!["--no-deps".to_owned()]; + meta.no_deps(); + let mut other_options = vec![]; if manifest.is_rust_manifest() { meta.env("RUSTC_BOOTSTRAP", "1"); other_options.push("-Zscript".to_owned()); From e685e90513f8a43dd75b7c5f88980666c48000b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 23 Jul 2025 09:25:02 +0200 Subject: [PATCH 032/201] Update `CONTRIBUTING.md` --- src/tools/miri/CONTRIBUTING.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md index 637c0dd2fdf1..7d78fdddbad3 100644 --- a/src/tools/miri/CONTRIBUTING.md +++ b/src/tools/miri/CONTRIBUTING.md @@ -297,14 +297,14 @@ You can also directly run Miri on a Rust source file: ## Advanced topic: Syncing with the rustc repo -We use the [`josh` proxy](https://github.com/josh-project/josh) to transmit changes between the +We use the [`josh-sync`](https://github.com/rust-lang/josh-sync) tool to transmit changes between the rustc and Miri repositories. You can install it as follows: ```sh -cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +cargo install --locked --git https://github.com/rust-lang/josh-sync ``` -Josh will automatically be started and stopped by `./miri`. +The commands below will automatically install and manage the [Josh](https://github.com/josh-project/josh) proxy that performs the actual work. ### Importing changes from the rustc repo @@ -312,10 +312,12 @@ Josh will automatically be started and stopped by `./miri`. We assume we start on an up-to-date master branch in the Miri repo. +1) First, create a branch for the pull, e.g. `git checkout -b rustup` +2) Then run the following: ```sh # Fetch and merge rustc side of the history. Takes ca 5 min the first time. # This will also update the `rustc-version` file. -./miri rustc-pull +rustc-josh-sync pull # Update local toolchain and apply formatting. ./miri toolchain && ./miri fmt git commit -am "rustup" @@ -328,12 +330,12 @@ needed. ### Exporting changes to the rustc repo -We will use the josh proxy to push to your fork of rustc. Run the following in the Miri repo, +We will use the `josh-sync` tool to push to your fork of rustc. Run the following in the Miri repo, assuming we are on an up-to-date master branch: ```sh # Push the Miri changes to your rustc fork (substitute your github handle for YOUR_NAME). -./miri rustc-push YOUR_NAME miri +rustc-josh-sync push miri YOUR_NAME ``` This will create a new branch called `miri` in your fork, and the output should include a link that From 0b88bea0d5169af3b3f9326274bda8b9c4a31823 Mon Sep 17 00:00:00 2001 From: Jake Goulding Date: Wed, 23 Jul 2025 11:35:50 -0400 Subject: [PATCH 033/201] Move `dist-apple-various` from x86_64 to aarch64 `macos-13` is going away soonish. --- src/ci/github-actions/jobs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 0a6ebe44b3d7..e16f00547c19 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -478,7 +478,7 @@ auto: NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 - <<: *job-macos + <<: *job-macos-m1 - name: x86_64-apple-1 env: From 48963fa8c2855e267c7ea92e6379ecc793df20d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 23 Jul 2025 09:34:36 +0200 Subject: [PATCH 034/201] Update CI workflow --- src/tools/miri/.github/workflows/ci.yml | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 11c0f08debe6..75f3792bca24 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -147,27 +147,40 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 256 # get a bit more of the history - - name: install josh-proxy - run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + - name: install josh-sync + run: cargo +stable install --locked --git https://github.com/rust-lang/josh-sync - name: setup bot git name and email run: | git config --global user.name 'The Miri Cronjob Bot' git config --global user.email 'miri@cron.bot' - name: Install nightly toolchain run: rustup toolchain install nightly --profile minimal - - name: get changes from rustc - run: ./miri rustc-pull - name: Install rustup-toolchain-install-master run: cargo install -f rustup-toolchain-install-master - - name: format changes (if any) - run: | - ./miri toolchain - ./miri fmt --check || (./miri fmt && git commit -am "fmt") - name: Push changes to a branch and create PR run: | - # `git diff --exit-code` "succeeds" if the diff is empty. - if git diff --exit-code HEAD^; then echo "Nothing changed in rustc, skipping PR"; exit 0; fi - # The diff is non-empty, create a PR. + # Temporarily disable early exit to examine the status code of rustc-josh-sync + set +e + rustc-josh-sync pull + exitcode=$? + set -e + + # If there were no changes to pull, rustc-josh-sync returns status code 2 + # In that case skip the rest of the job + if [ $exitcode -eq 2 ]; then + echo "Nothing changed in rustc, skipping PR" + exit 0 + elif [ $exitcode -ne 0 ]; then + # If return code was not 0 or 2, rustc-josh-sync actually failed + echo "rustc-josh-sync failed" + exit ${exitcode} + fi + + # Format changes + ./miri toolchain + ./miri fmt --check || (./miri fmt && git commit -am "fmt") + + # Create a PR BRANCH="rustup-$(date -u +%Y-%m-%d)" git switch -c $BRANCH git push -u origin $BRANCH From 8664aa72dcd274be80f24b395bac8cbf10bf93ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 23 Jul 2025 22:00:50 +0200 Subject: [PATCH 035/201] Remove Zulip API keys and use `set -x` --- src/tools/miri/.github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 75f3792bca24..73c5936cd994 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -159,20 +159,22 @@ jobs: run: cargo install -f rustup-toolchain-install-master - name: Push changes to a branch and create PR run: | + # Make it easier to see what happens. + set -x # Temporarily disable early exit to examine the status code of rustc-josh-sync set +e rustc-josh-sync pull exitcode=$? set -e - # If there were no changes to pull, rustc-josh-sync returns status code 2 - # In that case skip the rest of the job + # If there were no changes to pull, rustc-josh-sync returns status code 2. + # In that case, skip the rest of the job. if [ $exitcode -eq 2 ]; then echo "Nothing changed in rustc, skipping PR" exit 0 elif [ $exitcode -ne 0 ]; then # If return code was not 0 or 2, rustc-josh-sync actually failed - echo "rustc-josh-sync failed" + echo "error: rustc-josh-sync failed" exit ${exitcode} fi @@ -187,8 +189,6 @@ jobs: gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} - ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} cron-fail-notify: name: cronjob failure notification From 477814a3e4e33993a2f37c906747a0c2b1e4102e Mon Sep 17 00:00:00 2001 From: Zachary S Date: Sat, 19 Jul 2025 01:09:06 -0500 Subject: [PATCH 036/201] Document (internally) that `Range*<&T> as RangeBounds` impls are intentionally not `T: ?Sized`, and document (publically) an alternative. --- library/core/src/ops/range.rs | 30 ++++++++++++++++++++++++++++++ library/core/src/range.rs | 18 ++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index ad3b6439a610..f33a33e6b752 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -1141,6 +1141,12 @@ impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..` with `(Bound::Included(start), Bound::Unbounded)`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1151,6 +1157,12 @@ impl RangeBounds for RangeFrom<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `..end` with `(Bound::Unbounded, Bound::Excluded(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeTo<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1161,6 +1173,12 @@ impl RangeBounds for RangeTo<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..end` with `(Bound::Included(start), Bound::Excluded(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for Range<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1171,6 +1189,12 @@ impl RangeBounds for Range<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -1181,6 +1205,12 @@ impl RangeBounds for RangeInclusive<&T> { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `..=end` with `(Bound::Unbounded, Bound::Included(end))`. #[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeToInclusive<&T> { fn start_bound(&self) -> Bound<&T> { diff --git a/library/core/src/range.rs b/library/core/src/range.rs index 5cd7956291ca..7158fa0fcf06 100644 --- a/library/core/src/range.rs +++ b/library/core/src/range.rs @@ -167,6 +167,12 @@ impl RangeBounds for Range { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..end` with `(Bound::Included(start), Bound::Excluded(end))`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for Range<&T> { fn start_bound(&self) -> Bound<&T> { @@ -346,6 +352,12 @@ impl RangeBounds for RangeInclusive { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..=end` with `(Bound::Included(start), Bound::Included(end))`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for RangeInclusive<&T> { fn start_bound(&self) -> Bound<&T> { @@ -491,6 +503,12 @@ impl RangeBounds for RangeFrom { } } +// This impl intentionally does not have `T: ?Sized`; +// see https://github.com/rust-lang/rust/pull/61584 for discussion of why. +// +/// If you need to use this implementation where `T` is unsized, +/// consider using the `RangeBounds` impl for a 2-tuple of [`Bound<&T>`][Bound], +/// i.e. replace `start..` with `(Bound::Included(start), Bound::Unbounded)`. #[unstable(feature = "new_range_api", issue = "125687")] impl RangeBounds for RangeFrom<&T> { fn start_bound(&self) -> Bound<&T> { From abddbc5bcab8e731d07c2bfbeae966ed28a33efd Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 24 Jul 2025 15:05:28 +0800 Subject: [PATCH 037/201] Fix generate_trait_from_impl whitespace after vis Input: ```rust struct Foo; impl F$0oo { pub fn a_func() -> Option<()> { Some(()) } } ``` Old: ```rust struct Foo; trait NewTrait { fn a_func() -> Option<()>; } impl NewTrait for Foo { fn a_func() -> Option<()> { Some(()) } } ``` This PR fixed: ```rust struct Foo; trait NewTrait { fn a_func() -> Option<()>; } impl NewTrait for Foo { fn a_func() -> Option<()> { Some(()) } } ``` --- .../src/handlers/generate_trait_from_impl.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 154b502e1bf9..92a4bd35b3e7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -3,7 +3,7 @@ use ide_db::assists::AssistId; use syntax::{ AstNode, SyntaxKind, T, ast::{ - self, HasGenericParams, HasName, + self, HasGenericParams, HasName, HasVisibility, edit_in_place::{HasVisibilityEdit, Indent}, make, }, @@ -164,6 +164,12 @@ pub(crate) fn generate_trait_from_impl(acc: &mut Assists, ctx: &AssistContext<'_ /// `E0449` Trait items always share the visibility of their trait fn remove_items_visibility(item: &ast::AssocItem) { if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) { + if let Some(vis) = has_vis.visibility() + && let Some(token) = vis.syntax().next_sibling_or_token() + && token.kind() == SyntaxKind::WHITESPACE + { + ted::remove(token); + } has_vis.set_visibility(None); } } @@ -333,11 +339,11 @@ impl F$0oo { struct Foo; trait NewTrait { - fn a_func() -> Option<()>; + fn a_func() -> Option<()>; } impl NewTrait for Foo { - fn a_func() -> Option<()> { + fn a_func() -> Option<()> { Some(()) } }"#, From d622bfd47746fd95ce2779cd21c68739e683f2c6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 22 Jul 2025 15:58:42 +0200 Subject: [PATCH 038/201] Display total time and compilation time of merged doctests --- src/librustdoc/doctest.rs | 84 ++++++++++++++++++++++++++------ src/librustdoc/doctest/runner.rs | 11 +++-- 2 files changed, 77 insertions(+), 18 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 38ba6b4503dc..a4284d93dfb9 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -11,7 +11,8 @@ use std::path::{Path, PathBuf}; use std::process::{self, Command, Stdio}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use std::{panic, str}; +use std::time::{Duration, Instant}; +use std::{fmt, panic, str}; pub(crate) use make::{BuildDocTestBuilder, DocTestBuilder}; pub(crate) use markdown::test as test_markdown; @@ -36,6 +37,50 @@ use crate::config::{Options as RustdocOptions, OutputFormat}; use crate::html::markdown::{ErrorCodes, Ignore, LangString, MdRelLine}; use crate::lint::init_lints; +/// Type used to display times (compilation and total) information for merged doctests. +struct MergedDoctestTimes { + total_time: Instant, + /// Total time spent compiling all merged doctests. + compilation_time: Duration, + /// This field is used to keep track of how many merged doctests we (tried to) compile. + added_compilation_times: usize, +} + +impl MergedDoctestTimes { + fn new() -> Self { + Self { + total_time: Instant::now(), + compilation_time: Duration::default(), + added_compilation_times: 0, + } + } + + fn add_compilation_time(&mut self, duration: Duration) { + self.compilation_time += duration; + self.added_compilation_times += 1; + } + + fn display_times(&self) { + // If no merged doctest was compiled, then there is nothing to display since the numbers + // displayed by `libtest` for standalone tests are already accurate (they include both + // compilation and runtime). + if self.added_compilation_times > 0 { + println!("{self}"); + } + } +} + +impl fmt::Display for MergedDoctestTimes { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "all doctests ran in {:.2}s; merged doctests compilation took {:.2}s", + self.total_time.elapsed().as_secs_f64(), + self.compilation_time.as_secs_f64(), + ) + } +} + /// Options that apply to all doctests in a crate or Markdown file (for `rustdoc foo.md`). #[derive(Clone)] pub(crate) struct GlobalTestOptions { @@ -295,6 +340,7 @@ pub(crate) fn run_tests( let mut nb_errors = 0; let mut ran_edition_tests = 0; + let mut times = MergedDoctestTimes::new(); let target_str = rustdoc_options.target.to_string(); for (MergeableTestKey { edition, global_crate_attrs_hash }, mut doctests) in mergeable_tests { @@ -314,13 +360,15 @@ pub(crate) fn run_tests( for (doctest, scraped_test) in &doctests { tests_runner.add_test(doctest, scraped_test, &target_str); } - if let Ok(success) = tests_runner.run_merged_tests( + let (duration, ret) = tests_runner.run_merged_tests( rustdoc_test_options, edition, &opts, &test_args, rustdoc_options, - ) { + ); + times.add_compilation_time(duration); + if let Ok(success) = ret { ran_edition_tests += 1; if !success { nb_errors += 1; @@ -354,11 +402,13 @@ pub(crate) fn run_tests( test::test_main_with_exit_callback(&test_args, standalone_tests, None, || { // We ensure temp dir destructor is called. std::mem::drop(temp_dir.take()); + times.display_times(); }); } if nb_errors != 0 { // We ensure temp dir destructor is called. std::mem::drop(temp_dir); + times.display_times(); // libtest::ERROR_EXIT_CODE is not public but it's the same value. std::process::exit(101); } @@ -496,16 +546,19 @@ impl RunnableDocTest { /// /// This is the function that calculates the compiler command line, invokes the compiler, then /// invokes the test or tests in a separate executable (if applicable). +/// +/// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test. fn run_test( doctest: RunnableDocTest, rustdoc_options: &RustdocOptions, supports_color: bool, report_unused_externs: impl Fn(UnusedExterns), -) -> Result<(), TestFailure> { +) -> (Duration, Result<(), TestFailure>) { let langstr = &doctest.langstr; // Make sure we emit well-formed executable names for our target. let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); let output_file = doctest.test_opts.outdir.path().join(rust_out); + let instant = Instant::now(); // Common arguments used for compiling the doctest runner. // On merged doctests, the compiler is invoked twice: once for the test code itself, @@ -589,7 +642,7 @@ fn run_test( if std::fs::write(&input_file, &doctest.full_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. - return Err(TestFailure::CompileError); + return (Duration::default(), Err(TestFailure::CompileError)); } if !rustdoc_options.nocapture { // If `nocapture` is disabled, then we don't display rustc's output when compiling @@ -660,7 +713,7 @@ fn run_test( if std::fs::write(&runner_input_file, merged_test_code).is_err() { // If we cannot write this file for any reason, we leave. All combined tests will be // tested as standalone tests. - return Err(TestFailure::CompileError); + return (instant.elapsed(), Err(TestFailure::CompileError)); } if !rustdoc_options.nocapture { // If `nocapture` is disabled, then we don't display rustc's output when compiling @@ -713,7 +766,7 @@ fn run_test( let _bomb = Bomb(&out); match (output.status.success(), langstr.compile_fail) { (true, true) => { - return Err(TestFailure::UnexpectedCompilePass); + return (instant.elapsed(), Err(TestFailure::UnexpectedCompilePass)); } (true, false) => {} (false, true) => { @@ -729,17 +782,18 @@ fn run_test( .collect(); if !missing_codes.is_empty() { - return Err(TestFailure::MissingErrorCodes(missing_codes)); + return (instant.elapsed(), Err(TestFailure::MissingErrorCodes(missing_codes))); } } } (false, false) => { - return Err(TestFailure::CompileError); + return (instant.elapsed(), Err(TestFailure::CompileError)); } } + let duration = instant.elapsed(); if doctest.no_run { - return Ok(()); + return (duration, Ok(())); } // Run the code! @@ -771,17 +825,17 @@ fn run_test( cmd.output() }; match result { - Err(e) => return Err(TestFailure::ExecutionError(e)), + Err(e) => return (duration, Err(TestFailure::ExecutionError(e))), Ok(out) => { if langstr.should_panic && out.status.success() { - return Err(TestFailure::UnexpectedRunPass); + return (duration, Err(TestFailure::UnexpectedRunPass)); } else if !langstr.should_panic && !out.status.success() { - return Err(TestFailure::ExecutionFailure(out)); + return (duration, Err(TestFailure::ExecutionFailure(out))); } } } - Ok(()) + (duration, Ok(())) } /// Converts a path intended to use as a command to absolute if it is @@ -1071,7 +1125,7 @@ fn doctest_run_fn( no_run: scraped_test.no_run(&rustdoc_options), merged_test_code: None, }; - let res = + let (_, res) = run_test(runnable_test, &rustdoc_options, doctest.supports_color, report_unused_externs); if let Err(err) = res { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index f0914474c793..fcfa424968e4 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -1,4 +1,5 @@ use std::fmt::Write; +use std::time::Duration; use rustc_data_structures::fx::FxIndexSet; use rustc_span::edition::Edition; @@ -67,6 +68,10 @@ impl DocTestRunner { self.nb_tests += 1; } + /// Returns a tuple containing the `Duration` of the compilation and the `Result` of the test. + /// + /// If compilation failed, it will return `Err`, otherwise it will return `Ok` containing if + /// the test ran successfully. pub(crate) fn run_merged_tests( &mut self, test_options: IndividualTestOptions, @@ -74,7 +79,7 @@ impl DocTestRunner { opts: &GlobalTestOptions, test_args: &[String], rustdoc_options: &RustdocOptions, - ) -> Result { + ) -> (Duration, Result) { let mut code = "\ #![allow(unused_extern_crates)] #![allow(internal_features)] @@ -204,9 +209,9 @@ std::process::Termination::report(test::test_main(test_args, tests, None)) no_run: false, merged_test_code: Some(code), }; - let ret = + let (duration, ret) = run_test(runnable_test, rustdoc_options, self.supports_color, |_: UnusedExterns| {}); - if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) } + (duration, if let Err(TestFailure::CompileError) = ret { Err(()) } else { Ok(ret.is_ok()) }) } } From 64e3078bbd6b5ac7246735c9ee0a16a091e6f45f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 22 Jul 2025 16:00:25 +0200 Subject: [PATCH 039/201] Update rustdoc ui tests --- tests/rustdoc-ui/2024-doctests-checks.rs | 2 + tests/rustdoc-ui/2024-doctests-checks.stdout | 5 +- .../2024-doctests-crate-attribute.rs | 2 + .../2024-doctests-crate-attribute.stdout | 5 +- tests/rustdoc-ui/doctest/dead-code-2024.rs | 2 + .../rustdoc-ui/doctest/dead-code-2024.stdout | 11 +-- tests/rustdoc-ui/doctest/dead-code-items.rs | 2 + .../rustdoc-ui/doctest/dead-code-items.stdout | 83 ++++++++++--------- .../rustdoc-ui/doctest/dead-code-module-2.rs | 2 + .../doctest/dead-code-module-2.stdout | 13 +-- tests/rustdoc-ui/doctest/dead-code-module.rs | 2 + .../doctest/dead-code-module.stdout | 11 +-- .../doctest/doctest-output-include-fail.rs | 2 + .../doctest-output-include-fail.stdout | 1 + .../doctest/edition-2024-error-output.rs | 2 + .../doctest/edition-2024-error-output.stdout | 7 +- .../doctest/failed-doctest-should-panic.rs | 2 + .../failed-doctest-should-panic.stdout | 9 +- ...iled-doctest-test-crate.edition2015.stdout | 8 +- ...iled-doctest-test-crate.edition2024.stdout | 9 +- .../doctest/failed-doctest-test-crate.rs | 2 + ...th-include-bytes-132203.edition2015.stdout | 8 +- ...th-include-bytes-132203.edition2024.stdout | 1 + .../relative-path-include-bytes-132203.rs | 2 + tests/rustdoc-ui/doctest/stdout-and-stderr.rs | 2 + .../doctest/stdout-and-stderr.stdout | 19 +++-- tests/rustdoc-ui/doctest/wrong-ast-2024.rs | 2 + .../rustdoc-ui/doctest/wrong-ast-2024.stdout | 15 ++-- 28 files changed, 135 insertions(+), 96 deletions(-) diff --git a/tests/rustdoc-ui/2024-doctests-checks.rs b/tests/rustdoc-ui/2024-doctests-checks.rs index 0c3a11771f34..61f90fe62310 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.rs +++ b/tests/rustdoc-ui/2024-doctests-checks.rs @@ -3,6 +3,8 @@ //@ compile-flags: --test --test-args=--test-threads=1 //@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" /// ``` diff --git a/tests/rustdoc-ui/2024-doctests-checks.stdout b/tests/rustdoc-ui/2024-doctests-checks.stdout index 534fe466fe70..c86eafd61b91 100644 --- a/tests/rustdoc-ui/2024-doctests-checks.stdout +++ b/tests/rustdoc-ui/2024-doctests-checks.stdout @@ -1,12 +1,13 @@ running 1 test -test $DIR/2024-doctests-checks.rs - Foo (line 8) ... ok +test $DIR/2024-doctests-checks.rs - Foo (line 10) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME running 1 test -test $DIR/2024-doctests-checks.rs - Foo (line 15) ... ok +test $DIR/2024-doctests-checks.rs - Foo (line 17) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/2024-doctests-crate-attribute.rs b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs index c9887cbc63ba..416d50cb0701 100644 --- a/tests/rustdoc-ui/2024-doctests-crate-attribute.rs +++ b/tests/rustdoc-ui/2024-doctests-crate-attribute.rs @@ -4,6 +4,8 @@ //@ normalize-stdout: "tests/rustdoc-ui" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" /// This doctest is used to ensure that if a crate attribute is present, /// it will not be part of the merged doctests. diff --git a/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout b/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout index c084ac4522e8..20618426312e 100644 --- a/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout +++ b/tests/rustdoc-ui/2024-doctests-crate-attribute.stdout @@ -1,12 +1,13 @@ running 1 test -test $DIR/2024-doctests-crate-attribute.rs - Foo (line 20) ... ok +test $DIR/2024-doctests-crate-attribute.rs - Foo (line 22) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME running 1 test -test $DIR/2024-doctests-crate-attribute.rs - Foo (line 11) ... ok +test $DIR/2024-doctests-crate-attribute.rs - Foo (line 13) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/dead-code-2024.rs b/tests/rustdoc-ui/doctest/dead-code-2024.rs index 079d44570bba..e02d2601c588 100644 --- a/tests/rustdoc-ui/doctest/dead-code-2024.rs +++ b/tests/rustdoc-ui/doctest/dead-code-2024.rs @@ -4,6 +4,8 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 #![doc(test(attr(allow(unused_variables), deny(warnings))))] diff --git a/tests/rustdoc-ui/doctest/dead-code-2024.stdout b/tests/rustdoc-ui/doctest/dead-code-2024.stdout index a943a177e10b..bf9cd65200b2 100644 --- a/tests/rustdoc-ui/doctest/dead-code-2024.stdout +++ b/tests/rustdoc-ui/doctest/dead-code-2024.stdout @@ -1,18 +1,18 @@ running 1 test -test $DIR/dead-code-2024.rs - f (line 13) - compile ... FAILED +test $DIR/dead-code-2024.rs - f (line 15) - compile ... FAILED failures: ----- $DIR/dead-code-2024.rs - f (line 13) stdout ---- +---- $DIR/dead-code-2024.rs - f (line 15) stdout ---- error: trait `T` is never used - --> $DIR/dead-code-2024.rs:14:7 + --> $DIR/dead-code-2024.rs:16:7 | LL | trait T { fn f(); } | ^ | note: the lint level is defined here - --> $DIR/dead-code-2024.rs:12:9 + --> $DIR/dead-code-2024.rs:14:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -23,7 +23,8 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/dead-code-2024.rs - f (line 13) + $DIR/dead-code-2024.rs - f (line 15) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/dead-code-items.rs b/tests/rustdoc-ui/doctest/dead-code-items.rs index 015504cbcedb..ff59bfaabc47 100644 --- a/tests/rustdoc-ui/doctest/dead-code-items.rs +++ b/tests/rustdoc-ui/doctest/dead-code-items.rs @@ -4,6 +4,8 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 #![doc(test(attr(deny(warnings))))] diff --git a/tests/rustdoc-ui/doctest/dead-code-items.stdout b/tests/rustdoc-ui/doctest/dead-code-items.stdout index 4b9d8be94dd3..ecfe09f09ce2 100644 --- a/tests/rustdoc-ui/doctest/dead-code-items.stdout +++ b/tests/rustdoc-ui/doctest/dead-code-items.stdout @@ -1,30 +1,30 @@ running 13 tests -test $DIR/dead-code-items.rs - A (line 32) - compile ... ok -test $DIR/dead-code-items.rs - A (line 88) - compile ... ok -test $DIR/dead-code-items.rs - A::field (line 39) - compile ... FAILED -test $DIR/dead-code-items.rs - A::method (line 94) - compile ... ok -test $DIR/dead-code-items.rs - C (line 22) - compile ... FAILED -test $DIR/dead-code-items.rs - Enum (line 70) - compile ... FAILED -test $DIR/dead-code-items.rs - Enum::Variant1 (line 77) - compile ... FAILED -test $DIR/dead-code-items.rs - MyTrait (line 103) - compile ... FAILED -test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) - compile ... FAILED -test $DIR/dead-code-items.rs - S (line 14) - compile ... ok -test $DIR/dead-code-items.rs - U (line 48) - compile ... ok -test $DIR/dead-code-items.rs - U::field (line 55) - compile ... FAILED -test $DIR/dead-code-items.rs - U::field2 (line 61) - compile ... ok +test $DIR/dead-code-items.rs - A (line 34) - compile ... ok +test $DIR/dead-code-items.rs - A (line 90) - compile ... ok +test $DIR/dead-code-items.rs - A::field (line 41) - compile ... FAILED +test $DIR/dead-code-items.rs - A::method (line 96) - compile ... ok +test $DIR/dead-code-items.rs - C (line 24) - compile ... FAILED +test $DIR/dead-code-items.rs - Enum (line 72) - compile ... FAILED +test $DIR/dead-code-items.rs - Enum::Variant1 (line 79) - compile ... FAILED +test $DIR/dead-code-items.rs - MyTrait (line 105) - compile ... FAILED +test $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) - compile ... FAILED +test $DIR/dead-code-items.rs - S (line 16) - compile ... ok +test $DIR/dead-code-items.rs - U (line 50) - compile ... ok +test $DIR/dead-code-items.rs - U::field (line 57) - compile ... FAILED +test $DIR/dead-code-items.rs - U::field2 (line 63) - compile ... ok failures: ----- $DIR/dead-code-items.rs - A::field (line 39) stdout ---- +---- $DIR/dead-code-items.rs - A::field (line 41) stdout ---- error: trait `DeadCodeInField` is never used - --> $DIR/dead-code-items.rs:40:7 + --> $DIR/dead-code-items.rs:42:7 | LL | trait DeadCodeInField {} | ^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/dead-code-items.rs:38:9 + --> $DIR/dead-code-items.rs:40:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ @@ -32,15 +32,15 @@ LL | #![deny(dead_code)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - C (line 22) stdout ---- +---- $DIR/dead-code-items.rs - C (line 24) stdout ---- error: unused variable: `unused_error` - --> $DIR/dead-code-items.rs:23:5 + --> $DIR/dead-code-items.rs:25:5 | LL | let unused_error = 5; | ^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_error` | note: the lint level is defined here - --> $DIR/dead-code-items.rs:20:9 + --> $DIR/dead-code-items.rs:22:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -49,15 +49,15 @@ LL | #![deny(warnings)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - Enum (line 70) stdout ---- +---- $DIR/dead-code-items.rs - Enum (line 72) stdout ---- error: unused variable: `not_dead_code_but_unused` - --> $DIR/dead-code-items.rs:71:5 + --> $DIR/dead-code-items.rs:73:5 | LL | let not_dead_code_but_unused = 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_not_dead_code_but_unused` | note: the lint level is defined here - --> $DIR/dead-code-items.rs:68:9 + --> $DIR/dead-code-items.rs:70:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -66,15 +66,15 @@ LL | #![deny(warnings)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - Enum::Variant1 (line 77) stdout ---- +---- $DIR/dead-code-items.rs - Enum::Variant1 (line 79) stdout ---- error: unused variable: `unused_in_variant` - --> $DIR/dead-code-items.rs:80:17 + --> $DIR/dead-code-items.rs:82:17 | LL | fn main() { let unused_in_variant = 5; } | ^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_variant` | note: the lint level is defined here - --> $DIR/dead-code-items.rs:75:9 + --> $DIR/dead-code-items.rs:77:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -83,15 +83,15 @@ LL | #![deny(warnings)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - MyTrait (line 103) stdout ---- +---- $DIR/dead-code-items.rs - MyTrait (line 105) stdout ---- error: trait `StillDeadCodeAtMyTrait` is never used - --> $DIR/dead-code-items.rs:104:7 + --> $DIR/dead-code-items.rs:106:7 | LL | trait StillDeadCodeAtMyTrait { } | ^^^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/dead-code-items.rs:102:9 + --> $DIR/dead-code-items.rs:104:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ @@ -99,15 +99,15 @@ LL | #![deny(dead_code)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) stdout ---- +---- $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) stdout ---- error: unused variable: `unused_in_impl` - --> $DIR/dead-code-items.rs:113:17 + --> $DIR/dead-code-items.rs:115:17 | LL | fn main() { let unused_in_impl = 5; } | ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused_in_impl` | note: the lint level is defined here - --> $DIR/dead-code-items.rs:108:9 + --> $DIR/dead-code-items.rs:110:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -116,15 +116,15 @@ LL | #![deny(warnings)] error: aborting due to 1 previous error Couldn't compile the test. ----- $DIR/dead-code-items.rs - U::field (line 55) stdout ---- +---- $DIR/dead-code-items.rs - U::field (line 57) stdout ---- error: trait `DeadCodeInUnionField` is never used - --> $DIR/dead-code-items.rs:56:7 + --> $DIR/dead-code-items.rs:58:7 | LL | trait DeadCodeInUnionField {} | ^^^^^^^^^^^^^^^^^^^^ | note: the lint level is defined here - --> $DIR/dead-code-items.rs:54:9 + --> $DIR/dead-code-items.rs:56:9 | LL | #![deny(dead_code)] | ^^^^^^^^^ @@ -134,13 +134,14 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/dead-code-items.rs - A::field (line 39) - $DIR/dead-code-items.rs - C (line 22) - $DIR/dead-code-items.rs - Enum (line 70) - $DIR/dead-code-items.rs - Enum::Variant1 (line 77) - $DIR/dead-code-items.rs - MyTrait (line 103) - $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 110) - $DIR/dead-code-items.rs - U::field (line 55) + $DIR/dead-code-items.rs - A::field (line 41) + $DIR/dead-code-items.rs - C (line 24) + $DIR/dead-code-items.rs - Enum (line 72) + $DIR/dead-code-items.rs - Enum::Variant1 (line 79) + $DIR/dead-code-items.rs - MyTrait (line 105) + $DIR/dead-code-items.rs - MyTrait::my_trait_fn (line 112) + $DIR/dead-code-items.rs - U::field (line 57) test result: FAILED. 6 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/dead-code-module-2.rs b/tests/rustdoc-ui/doctest/dead-code-module-2.rs index de7b11b91ec5..fd9c313ec9a4 100644 --- a/tests/rustdoc-ui/doctest/dead-code-module-2.rs +++ b/tests/rustdoc-ui/doctest/dead-code-module-2.rs @@ -4,6 +4,8 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 #![doc(test(attr(allow(unused_variables))))] diff --git a/tests/rustdoc-ui/doctest/dead-code-module-2.stdout b/tests/rustdoc-ui/doctest/dead-code-module-2.stdout index d44068dcbf5d..cf737996d5c9 100644 --- a/tests/rustdoc-ui/doctest/dead-code-module-2.stdout +++ b/tests/rustdoc-ui/doctest/dead-code-module-2.stdout @@ -1,24 +1,24 @@ running 1 test -test $DIR/dead-code-module-2.rs - g (line 24) - compile ... ok +test $DIR/dead-code-module-2.rs - g (line 26) - compile ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME running 1 test -test $DIR/dead-code-module-2.rs - my_mod::f (line 16) - compile ... FAILED +test $DIR/dead-code-module-2.rs - my_mod::f (line 18) - compile ... FAILED failures: ----- $DIR/dead-code-module-2.rs - my_mod::f (line 16) stdout ---- +---- $DIR/dead-code-module-2.rs - my_mod::f (line 18) stdout ---- error: trait `T` is never used - --> $DIR/dead-code-module-2.rs:17:7 + --> $DIR/dead-code-module-2.rs:19:7 | LL | trait T { fn f(); } | ^ | note: the lint level is defined here - --> $DIR/dead-code-module-2.rs:15:9 + --> $DIR/dead-code-module-2.rs:17:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -29,7 +29,8 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/dead-code-module-2.rs - my_mod::f (line 16) + $DIR/dead-code-module-2.rs - my_mod::f (line 18) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/dead-code-module.rs b/tests/rustdoc-ui/doctest/dead-code-module.rs index f825749a6a25..d3103ad28e9b 100644 --- a/tests/rustdoc-ui/doctest/dead-code-module.rs +++ b/tests/rustdoc-ui/doctest/dead-code-module.rs @@ -4,6 +4,8 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 mod my_mod { diff --git a/tests/rustdoc-ui/doctest/dead-code-module.stdout b/tests/rustdoc-ui/doctest/dead-code-module.stdout index b5ccf225d25c..83c6af3775e0 100644 --- a/tests/rustdoc-ui/doctest/dead-code-module.stdout +++ b/tests/rustdoc-ui/doctest/dead-code-module.stdout @@ -1,18 +1,18 @@ running 1 test -test $DIR/dead-code-module.rs - my_mod::f (line 14) - compile ... FAILED +test $DIR/dead-code-module.rs - my_mod::f (line 16) - compile ... FAILED failures: ----- $DIR/dead-code-module.rs - my_mod::f (line 14) stdout ---- +---- $DIR/dead-code-module.rs - my_mod::f (line 16) stdout ---- error: trait `T` is never used - --> $DIR/dead-code-module.rs:15:7 + --> $DIR/dead-code-module.rs:17:7 | LL | trait T { fn f(); } | ^ | note: the lint level is defined here - --> $DIR/dead-code-module.rs:13:9 + --> $DIR/dead-code-module.rs:15:9 | LL | #![deny(warnings)] | ^^^^^^^^ @@ -23,7 +23,8 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/dead-code-module.rs - my_mod::f (line 14) + $DIR/dead-code-module.rs - my_mod::f (line 16) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs index a47bac3daefe..2f0d6756b27f 100644 --- a/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.rs @@ -2,6 +2,8 @@ //@ compile-flags:--test --test-args=--test-threads=1 //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 // https://github.com/rust-lang/rust/issues/130470 diff --git a/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout index 22d15f8743c6..ceaf60b12015 100644 --- a/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout +++ b/tests/rustdoc-ui/doctest/doctest-output-include-fail.stdout @@ -22,3 +22,4 @@ failures: test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.rs b/tests/rustdoc-ui/doctest/edition-2024-error-output.rs index 82a85debcd19..e1e57ad01cdd 100644 --- a/tests/rustdoc-ui/doctest/edition-2024-error-output.rs +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.rs @@ -6,6 +6,8 @@ //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ rustc-env:RUST_BACKTRACE=0 //@ failure-status: 101 diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout index 273d70712373..ab6aca239afb 100644 --- a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout @@ -1,10 +1,10 @@ running 1 test -test $DIR/edition-2024-error-output.rs - (line 12) ... FAILED +test $DIR/edition-2024-error-output.rs - (line 14) ... FAILED failures: ----- $DIR/edition-2024-error-output.rs - (line 12) stdout ---- +---- $DIR/edition-2024-error-output.rs - (line 14) stdout ---- Test executable failed (exit status: 101). stderr: @@ -18,7 +18,8 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: - $DIR/edition-2024-error-output.rs - (line 12) + $DIR/edition-2024-error-output.rs - (line 14) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs index 793f86546610..0504c3dc7303 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.rs @@ -5,6 +5,8 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 /// ```should_panic diff --git a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout index 2b04b77c9dc5..9047fe0dcdd9 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-should-panic.stdout @@ -1,14 +1,15 @@ running 1 test -test $DIR/failed-doctest-should-panic.rs - Foo (line 10) - should panic ... FAILED +test $DIR/failed-doctest-should-panic.rs - Foo (line 12) - should panic ... FAILED failures: ----- $DIR/failed-doctest-should-panic.rs - Foo (line 10) stdout ---- -note: test did not panic as expected at $DIR/failed-doctest-should-panic.rs:10:0 +---- $DIR/failed-doctest-should-panic.rs - Foo (line 12) stdout ---- +note: test did not panic as expected at $DIR/failed-doctest-should-panic.rs:12:0 failures: - $DIR/failed-doctest-should-panic.rs - Foo (line 10) + $DIR/failed-doctest-should-panic.rs - Foo (line 12) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2015.stdout b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2015.stdout index ce767fb8443d..d80c0da323d3 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2015.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2015.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/failed-doctest-test-crate.rs - m (line 14) ... FAILED +test $DIR/failed-doctest-test-crate.rs - m (line 16) ... FAILED failures: ----- $DIR/failed-doctest-test-crate.rs - m (line 14) stdout ---- +---- $DIR/failed-doctest-test-crate.rs - m (line 16) stdout ---- error[E0432]: unresolved import `test` - --> $DIR/failed-doctest-test-crate.rs:15:5 + --> $DIR/failed-doctest-test-crate.rs:17:5 | LL | use test::*; | ^^^^ use of unresolved module or unlinked crate `test` @@ -22,7 +22,7 @@ For more information about this error, try `rustc --explain E0432`. Couldn't compile the test. failures: - $DIR/failed-doctest-test-crate.rs - m (line 14) + $DIR/failed-doctest-test-crate.rs - m (line 16) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout index 80642e93bbde..724bb9bee62d 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.edition2024.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/failed-doctest-test-crate.rs - m (line 14) ... FAILED +test $DIR/failed-doctest-test-crate.rs - m (line 16) ... FAILED failures: ----- $DIR/failed-doctest-test-crate.rs - m (line 14) stdout ---- +---- $DIR/failed-doctest-test-crate.rs - m (line 16) stdout ---- error[E0432]: unresolved import `test` - --> $DIR/failed-doctest-test-crate.rs:15:5 + --> $DIR/failed-doctest-test-crate.rs:17:5 | LL | use test::*; | ^^^^ use of unresolved module or unlinked crate `test` @@ -19,7 +19,8 @@ For more information about this error, try `rustc --explain E0432`. Couldn't compile the test. failures: - $DIR/failed-doctest-test-crate.rs - m (line 14) + $DIR/failed-doctest-test-crate.rs - m (line 16) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs index 6966d3df11ce..75f7ac396f55 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-test-crate.rs @@ -7,6 +7,8 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 /// diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout index ff26e7e32318..0d00a9fc9c45 100644 --- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2015.stdout @@ -1,12 +1,12 @@ running 1 test -test $DIR/relative-path-include-bytes-132203.rs - (line 18) ... FAILED +test $DIR/relative-path-include-bytes-132203.rs - (line 20) ... FAILED failures: ----- $DIR/relative-path-include-bytes-132203.rs - (line 18) stdout ---- +---- $DIR/relative-path-include-bytes-132203.rs - (line 20) stdout ---- error: couldn't read `$DIR/relative-dir-empty-file`: $FILE_NOT_FOUND_MSG (os error 2) - --> $DIR/relative-path-include-bytes-132203.rs:19:9 + --> $DIR/relative-path-include-bytes-132203.rs:21:9 | LL | let x = include_bytes!("relative-dir-empty-file"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/relative-path-include-bytes-132203.rs - (line 18) + $DIR/relative-path-include-bytes-132203.rs - (line 20) test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout index e4c657030819..fa5bd7c93fa3 100644 --- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.edition2024.stdout @@ -4,3 +4,4 @@ test $DIR/auxiliary/relative-dir.md - (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs index ceacd69a5fd5..321edc3ee843 100644 --- a/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs +++ b/tests/rustdoc-ui/doctest/relative-path-include-bytes-132203.rs @@ -9,6 +9,8 @@ //@ normalize-stdout: "tests.rustdoc-ui.doctest." -> "$$DIR/" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: "`: .* \(os error 2\)" -> "`: $$FILE_NOT_FOUND_MSG (os error 2)" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" // https://github.com/rust-lang/rust/issues/132203 // This version, because it's edition2024, passes thanks to the new diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.rs b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs index 9b0c69d88391..a4eda8c7f836 100644 --- a/tests/rustdoc-ui/doctest/stdout-and-stderr.rs +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs @@ -9,6 +9,8 @@ //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 //@ rustc-env:RUST_BACKTRACE=0 diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout index b2febe1344f6..a35a4d7c3cb8 100644 --- a/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout @@ -1,12 +1,12 @@ running 3 tests -test $DIR/stdout-and-stderr.rs - (line 15) ... FAILED -test $DIR/stdout-and-stderr.rs - (line 20) ... FAILED -test $DIR/stdout-and-stderr.rs - (line 24) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 17) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 22) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 26) ... FAILED failures: ----- $DIR/stdout-and-stderr.rs - (line 15) stdout ---- +---- $DIR/stdout-and-stderr.rs - (line 17) stdout ---- Test executable failed (exit status: 101). stdout: @@ -21,7 +21,7 @@ assertion `left == right` failed note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ----- $DIR/stdout-and-stderr.rs - (line 20) stdout ---- +---- $DIR/stdout-and-stderr.rs - (line 22) stdout ---- Test executable failed (exit status: 101). stderr: @@ -33,14 +33,15 @@ assertion `left == right` failed note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace ----- $DIR/stdout-and-stderr.rs - (line 24) stdout ---- +---- $DIR/stdout-and-stderr.rs - (line 26) stdout ---- Test executable failed (exit status: 1). failures: - $DIR/stdout-and-stderr.rs - (line 15) - $DIR/stdout-and-stderr.rs - (line 20) - $DIR/stdout-and-stderr.rs - (line 24) + $DIR/stdout-and-stderr.rs - (line 17) + $DIR/stdout-and-stderr.rs - (line 22) + $DIR/stdout-and-stderr.rs - (line 26) test result: FAILED. 0 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs index 3b4fb3f34433..df30e01b25ed 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.rs +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.rs @@ -3,6 +3,8 @@ //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: ".rs:\d+:\d+" -> ".rs:$$LINE:$$COL" +//@ normalize-stdout: "ran in \d+\.\d+s" -> "ran in $$TIME" +//@ normalize-stdout: "compilation took \d+\.\d+s" -> "compilation took $$TIME" //@ failure-status: 101 /// ``` diff --git a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout index 62e1fb10b9f4..13567b41e51f 100644 --- a/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout +++ b/tests/rustdoc-ui/doctest/wrong-ast-2024.stdout @@ -1,17 +1,17 @@ running 1 test -test $DIR/wrong-ast-2024.rs - three (line 18) - should panic ... ok +test $DIR/wrong-ast-2024.rs - three (line 20) - should panic ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME running 2 tests -test $DIR/wrong-ast-2024.rs - one (line 8) ... FAILED -test $DIR/wrong-ast-2024.rs - two (line 13) ... FAILED +test $DIR/wrong-ast-2024.rs - one (line 10) ... FAILED +test $DIR/wrong-ast-2024.rs - two (line 15) ... FAILED failures: ----- $DIR/wrong-ast-2024.rs - one (line 8) stdout ---- +---- $DIR/wrong-ast-2024.rs - one (line 10) stdout ---- error[E0758]: unterminated block comment --> $DIR/wrong-ast-2024.rs:$LINE:$COL | @@ -22,7 +22,7 @@ error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0758`. Couldn't compile the test. ----- $DIR/wrong-ast-2024.rs - two (line 13) stdout ---- +---- $DIR/wrong-ast-2024.rs - two (line 15) stdout ---- error: unexpected closing delimiter: `}` --> $DIR/wrong-ast-2024.rs:$LINE:$COL | @@ -34,8 +34,9 @@ error: aborting due to 1 previous error Couldn't compile the test. failures: - $DIR/wrong-ast-2024.rs - one (line 8) - $DIR/wrong-ast-2024.rs - two (line 13) + $DIR/wrong-ast-2024.rs - one (line 10) + $DIR/wrong-ast-2024.rs - two (line 15) test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +all doctests ran in $TIME; merged doctests compilation took $TIME From 5e1ffef03c79a92143b87edd660a147d889e17d9 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Jul 2025 09:14:46 +0000 Subject: [PATCH 040/201] Improve unit_tests tidy lint Make it clearer where unit tests are allowed and restrict standard library unit tests inside the same package to std_detect, std and test. --- src/tools/tidy/src/main.rs | 6 +-- src/tools/tidy/src/unit_tests.rs | 84 ++++++++++++++++++++------------ 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs index 13b20f33bd0f..1a32372d2093 100644 --- a/src/tools/tidy/src/main.rs +++ b/src/tools/tidy/src/main.rs @@ -127,9 +127,9 @@ fn main() { check!(pal, &library_path); // Checks that need to be done for both the compiler and std libraries. - check!(unit_tests, &src_path); - check!(unit_tests, &compiler_path); - check!(unit_tests, &library_path); + check!(unit_tests, &src_path, false); + check!(unit_tests, &compiler_path, false); + check!(unit_tests, &library_path, true); if bins::check_filesystem_support(&[&root_path], &output_directory) { check!(bins, &root_path); diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index df9146b51474..90da1bc765a9 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -1,44 +1,60 @@ //! Tidy check to ensure `#[test]` and `#[bench]` are not used directly inside -//! `core` or `alloc`. +//! of the standard library. //! //! `core` and `alloc` cannot be tested directly due to duplicating lang items. //! All tests and benchmarks must be written externally in //! `{coretests,alloctests}/{tests,benches}`. //! -//! Outside of `core` and `alloc`, tests and benchmarks should be outlined into -//! separate files named `tests.rs` or `benches.rs`, or directories named +//! Outside of the standard library, tests and benchmarks should be outlined +//! into separate files named `tests.rs` or `benches.rs`, or directories named //! `tests` or `benches` unconfigured during normal build. use std::path::Path; use crate::walk::{filter_dirs, walk}; -pub fn check(root_path: &Path, bad: &mut bool) { - let core = root_path.join("core"); - let core_copy = core.clone(); - let is_core = move |path: &Path| path.starts_with(&core); - let alloc = root_path.join("alloc"); - let alloc_copy = alloc.clone(); - let is_alloc = move |path: &Path| path.starts_with(&alloc); - +pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { let skip = move |path: &Path, is_dir| { let file_name = path.file_name().unwrap_or_default(); + + // Skip excluded directories and non-rust files if is_dir { - filter_dirs(path) - || path.ends_with("src/doc") - || (file_name == "tests" || file_name == "benches") - && !is_core(path) - && !is_alloc(path) + if filter_dirs(path) || path.ends_with("src/doc") { + return true; + } } else { let extension = path.extension().unwrap_or_default(); - extension != "rs" - || (file_name == "tests.rs" || file_name == "benches.rs") - && !is_core(path) - && !is_alloc(path) - // Tests which use non-public internals and, as such, need to - // have the types in the same crate as the tests themselves. See - // the comment in alloctests/lib.rs. - || path.ends_with("library/alloc/src/collections/btree/borrow/tests.rs") + if extension != "rs" { + return true; + } + } + + // Tests in a separate package are always allowed + if is_dir && file_name != "tests" && file_name.as_encoded_bytes().ends_with(b"tests") { + return true; + } + + if !stdlib { + // Outside of the standard library tests may also be in separate files in the same crate + if is_dir { + if file_name == "tests" || file_name == "benches" { + return true; + } + } else { + if file_name == "tests.rs" || file_name == "benches.rs" { + return true; + } + } + } + + if is_dir { + // FIXME remove those exceptions once no longer necessary + file_name == "std_detect" || file_name == "std" || file_name == "test" + } else { + // Tests which use non-public internals and, as such, need to + // have the types in the same crate as the tests themselves. See + // the comment in alloctests/lib.rs. + path.ends_with("library/alloc/src/collections/btree/borrow/tests.rs") || path.ends_with("library/alloc/src/collections/btree/map/tests.rs") || path.ends_with("library/alloc/src/collections/btree/node/tests.rs") || path.ends_with("library/alloc/src/collections/btree/set/tests.rs") @@ -50,22 +66,30 @@ pub fn check(root_path: &Path, bad: &mut bool) { walk(root_path, skip, &mut |entry, contents| { let path = entry.path(); - let is_core = path.starts_with(&core_copy); - let is_alloc = path.starts_with(&alloc_copy); + let package = path + .strip_prefix(root_path) + .unwrap() + .components() + .next() + .unwrap() + .as_os_str() + .to_str() + .unwrap(); for (i, line) in contents.lines().enumerate() { let line = line.trim(); let is_test = || line.contains("#[test]") && !line.contains("`#[test]"); let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]"); let manual_skip = line.contains("//tidy:skip"); if !line.starts_with("//") && (is_test() || is_bench()) && !manual_skip { - let explanation = if is_core { - "`core` unit tests and benchmarks must be placed into `coretests`" - } else if is_alloc { - "`alloc` unit tests and benchmarks must be placed into `alloctests`" + let explanation = if stdlib { + format!( + "`{package}` unit tests and benchmarks must be placed into `{package}tests`" + ) } else { "unit tests and benchmarks must be placed into \ separate files or directories named \ `tests.rs`, `benches.rs`, `tests` or `benches`" + .to_owned() }; let name = if is_test() { "test" } else { "bench" }; tidy_error!( From c5d7021cddd97a4ec8dd64f878711ec983986fcd Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Jul 2025 09:15:28 +0000 Subject: [PATCH 041/201] Disable unit tests for stdlib packages that don't contain any --- library/rustc-std-workspace-alloc/Cargo.toml | 3 +++ library/rustc-std-workspace-core/Cargo.toml | 3 +++ library/rustc-std-workspace-std/Cargo.toml | 3 +++ library/sysroot/Cargo.toml | 2 ++ library/windows_targets/Cargo.toml | 5 +++++ 5 files changed, 16 insertions(+) diff --git a/library/rustc-std-workspace-alloc/Cargo.toml b/library/rustc-std-workspace-alloc/Cargo.toml index 5a177808d1bd..a5b51059119c 100644 --- a/library/rustc-std-workspace-alloc/Cargo.toml +++ b/library/rustc-std-workspace-alloc/Cargo.toml @@ -9,6 +9,9 @@ edition = "2024" [lib] path = "lib.rs" +test = false +bench = false +doc = false [dependencies] alloc = { path = "../alloc" } diff --git a/library/rustc-std-workspace-core/Cargo.toml b/library/rustc-std-workspace-core/Cargo.toml index 1ddc112380f1..d68965c63457 100644 --- a/library/rustc-std-workspace-core/Cargo.toml +++ b/library/rustc-std-workspace-core/Cargo.toml @@ -11,6 +11,9 @@ edition = "2024" [lib] path = "lib.rs" +test = false +bench = false +doc = false [dependencies] core = { path = "../core", public = true } diff --git a/library/rustc-std-workspace-std/Cargo.toml b/library/rustc-std-workspace-std/Cargo.toml index f70994e1f886..6079dc85d906 100644 --- a/library/rustc-std-workspace-std/Cargo.toml +++ b/library/rustc-std-workspace-std/Cargo.toml @@ -9,6 +9,9 @@ edition = "2024" [lib] path = "lib.rs" +test = false +bench = false +doc = false [dependencies] std = { path = "../std" } diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 032f5272a9cd..2952556f699d 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -6,6 +6,8 @@ version = "0.0.0" edition = "2024" [lib] +test = false +bench = false # make sure this crate isn't included in public standard library docs doc = false diff --git a/library/windows_targets/Cargo.toml b/library/windows_targets/Cargo.toml index 705c9e043811..1c804a0ab391 100644 --- a/library/windows_targets/Cargo.toml +++ b/library/windows_targets/Cargo.toml @@ -4,6 +4,11 @@ description = "A drop-in replacement for the real windows-targets crate for use version = "0.0.0" edition = "2024" +[lib] +test = false +bench = false +doc = false + [features] # Enable using raw-dylib for Windows imports. # This will eventually be the default. From fd114d465a7facfd7be01a290bf42ae6a1e9d649 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 24 Jul 2025 14:00:15 +0200 Subject: [PATCH 042/201] make the missing-MIR message more clear --- src/tools/miri/src/eval.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3c80e60b772a..f78908aba8ed 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -334,8 +334,8 @@ pub fn create_ecx<'tcx>( helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS); if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) { tcx.dcx().fatal( - "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \ - Use `cargo miri setup` to prepare a sysroot that is suitable for Miri." + "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing.\n\ + Note that directly invoking the `miri` binary is not supported; please use `cargo miri` instead." ); } From b481c5b8ba322612fb3bc167e323f7a548a2390c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:01:08 +0000 Subject: [PATCH 043/201] std_detect testing improvements * Fix riscv testing. Previously the mod tests; would be looking for src/detect/os/tests.rs. * Replace a test with an unnamed const item. It is testing that no warnings are emitted. It doesn't contain any checks that need to run at runtime. Replacing the test allows removing the tidy:skip directive for test locations. --- library/std_detect/src/detect/macros.rs | 5 ++--- library/std_detect/src/detect/os/riscv.rs | 1 + src/tools/tidy/src/unit_tests.rs | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/library/std_detect/src/detect/macros.rs b/library/std_detect/src/detect/macros.rs index c2a006d3753a..17140e15653d 100644 --- a/library/std_detect/src/detect/macros.rs +++ b/library/std_detect/src/detect/macros.rs @@ -131,14 +131,13 @@ macro_rules! features { }; } - #[test] //tidy:skip #[deny(unexpected_cfgs)] #[deny(unfulfilled_lint_expectations)] - fn unexpected_cfgs() { + const _: () = { $( check_cfg_feature!($feature, $feature_lit $(, without cfg check: $feature_cfg_check)? $(: $($target_feature_lit),*)?); )* - } + }; /// Each variant denotes a position in a bitset for a particular feature. /// diff --git a/library/std_detect/src/detect/os/riscv.rs b/library/std_detect/src/detect/os/riscv.rs index 46b7dd71eb35..dc9a4036d86a 100644 --- a/library/std_detect/src/detect/os/riscv.rs +++ b/library/std_detect/src/detect/os/riscv.rs @@ -135,4 +135,5 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize } #[cfg(test)] +#[path = "riscv/tests.rs"] mod tests; diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs index 90da1bc765a9..3d14a4673193 100644 --- a/src/tools/tidy/src/unit_tests.rs +++ b/src/tools/tidy/src/unit_tests.rs @@ -79,8 +79,7 @@ pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) { let line = line.trim(); let is_test = || line.contains("#[test]") && !line.contains("`#[test]"); let is_bench = || line.contains("#[bench]") && !line.contains("`#[bench]"); - let manual_skip = line.contains("//tidy:skip"); - if !line.starts_with("//") && (is_test() || is_bench()) && !manual_skip { + if !line.starts_with("//") && (is_test() || is_bench()) { let explanation = if stdlib { format!( "`{package}` unit tests and benchmarks must be placed into `{package}tests`" From a2320b25533df01d9b96707187b64e03595c9bee Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sun, 1 Oct 2023 14:46:12 +0000 Subject: [PATCH 044/201] Remove eval_always from check_private_in_public. --- compiler/rustc_middle/src/query/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b0d579a546fe..587349d4cf41 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1391,7 +1391,6 @@ rustc_queries! { desc { "checking effective visibilities" } } query check_private_in_public(_: ()) { - eval_always desc { "checking for private elements in public interfaces" } } From 0258894b762a163df714a86589a9e8158b354499 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 25 Jul 2025 05:01:00 +0000 Subject: [PATCH 045/201] Prepare for merging from rust-lang/rust This updates the rust-version file to b56aaec52bc0fa35591a872fb4aac81f606e265c. --- 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 bd902ca1fd5a..634ce1fa0628 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -9748d87dc70a9a6725c5dbd76ce29d04752b4f90 +b56aaec52bc0fa35591a872fb4aac81f606e265c From 0dba9f539f92c2abe7a8d3997a91e6884caa1fcf Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Fri, 25 Jul 2025 05:08:59 +0000 Subject: [PATCH 046/201] fmt --- src/tools/miri/src/machine.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 7271d3f619c7..142c6ddf9330 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -76,13 +76,8 @@ pub struct FrameExtra<'tcx> { impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Omitting `timing`, it does not support `Debug`. - let FrameExtra { - borrow_tracker, - catch_unwind, - timing: _, - is_user_relevant, - data_race, - } = self; + let FrameExtra { borrow_tracker, catch_unwind, timing: _, is_user_relevant, data_race } = + self; f.debug_struct("FrameData") .field("borrow_tracker", borrow_tracker) .field("catch_unwind", catch_unwind) From 60b054533fb607d0fac9716c1a6b94e9efa3b7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 25 Jul 2025 08:27:18 +0200 Subject: [PATCH 047/201] Fix cronjob Zulip message --- src/tools/miri/.github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 9f74642f0f15..e2e81370c600 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -219,7 +219,7 @@ jobs: It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed. This likely means that rustc changed the miri directory and - we now need to do a [`./miri rustc-pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo). + we now need to do a [`rustc-josh-sync pull`](https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#importing-changes-from-the-rustc-repo). Would you mind investigating this issue? From 2a81b4fad6bc5d089808d10cd880de040339cea6 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 22 Jul 2025 13:43:30 +0200 Subject: [PATCH 048/201] Use i64 for tracing chrome "id" Perfetto gives an error if an id does not fit in an 64-bit signed integer in 2's complement. --- src/tools/miri/src/bin/log/tracing_chrome.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs index 5a96633c99e8..3379816550cf 100644 --- a/src/tools/miri/src/bin/log/tracing_chrome.rs +++ b/src/tools/miri/src/bin/log/tracing_chrome.rs @@ -12,6 +12,7 @@ //! ```rust //! tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */) //! ``` +//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto //! //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would @@ -285,9 +286,9 @@ struct Callsite { } enum Message { - Enter(f64, Callsite, Option), + Enter(f64, Callsite, Option), Event(f64, Callsite), - Exit(f64, Callsite, Option), + Exit(f64, Callsite, Option), NewThread(usize, String), Flush, Drop, @@ -519,14 +520,17 @@ where } } - fn get_root_id(&self, span: SpanRef) -> Option { + fn get_root_id(&self, span: SpanRef) -> Option { + // Returns `Option` instead of `Option` because apparently Perfetto gives an + // error if an id does not fit in a 64-bit signed integer in 2's complement. We cast the + // span id from `u64` to `i64` with wraparound, since negative values are fine. match self.trace_style { TraceStyle::Threaded => { if span.fields().field("tracing_separate_thread").is_some() { // assign an independent "id" to spans with argument "tracing_separate_thread", // so they appear a separate trace line in trace visualization tools, see // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1 - Some(span.id().into_u64()) + Some(span.id().into_u64().cast_signed()) // the comment above explains the cast } else { None } @@ -539,6 +543,7 @@ where .unwrap_or(span) .id() .into_u64() + .cast_signed() // the comment above explains the cast ), } } From 00de833b60b6198f092901e1b2f6a10e6f3a89da Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 22 Jul 2025 13:45:37 +0200 Subject: [PATCH 049/201] Fix missing $ in enter_trace_span! --- src/tools/miri/src/helpers.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ccfff7fa94bd..6e80bc5da9eb 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1465,7 +1465,7 @@ pub struct MaybeEnteredTraceSpan { #[macro_export] macro_rules! enter_trace_span { ($name:ident :: $subname:ident $($tt:tt)*) => {{ - enter_trace_span!(stringify!($name), $name = %stringify!(subname) $($tt)*) + enter_trace_span!(stringify!($name), $name = %stringify!($subname) $($tt)*) }}; ($($tt:tt)*) => { From c7b81f38f8f5dee79be0fbddfba551eea3f98bf8 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 22 Jul 2025 18:42:11 +0200 Subject: [PATCH 050/201] Fix double "fatal error: " in message --- src/tools/miri/src/bin/log/setup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/src/bin/log/setup.rs b/src/tools/miri/src/bin/log/setup.rs index da0ba528b2c4..a9392d010f8e 100644 --- a/src/tools/miri/src/bin/log/setup.rs +++ b/src/tools/miri/src/bin/log/setup.rs @@ -60,7 +60,7 @@ fn init_logger_once(early_dcx: &EarlyDiagCtxt) { #[cfg(not(feature = "tracing"))] { crate::fatal_error!( - "fatal error: cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature" + "Cannot enable MIRI_TRACING since Miri was not built with the \"tracing\" feature" ); } From d2ba3c8672b09373311c930733f64aa10a4799a0 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 25 Jul 2025 08:43:37 +0200 Subject: [PATCH 051/201] fix target json --- src/tools/miri/tests/x86_64-unknown-kernel.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/x86_64-unknown-kernel.json b/src/tools/miri/tests/x86_64-unknown-kernel.json index 8da67d3a1c6b..a5eaceb4f68e 100644 --- a/src/tools/miri/tests/x86_64-unknown-kernel.json +++ b/src/tools/miri/tests/x86_64-unknown-kernel.json @@ -2,7 +2,7 @@ "llvm-target": "x86_64-unknown-none", "target-endian": "little", "target-pointer-width": "64", - "target-c-int-width": "32", + "target-c-int-width": 32, "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", "arch": "x86_64", "os": "none", From 3c8fdcbed6363b9a74c455ccd6fd5df0f6881d62 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Wed, 23 Jul 2025 16:16:32 +0900 Subject: [PATCH 052/201] split ted from gen_trait_fn_body --- .../src/handlers/add_missing_impl_members.rs | 11 +-- .../replace_derive_with_manual_impl.rs | 60 +++++----------- .../crates/ide-assists/src/utils.rs | 13 ++-- .../src/utils/gen_trait_fn_body.rs | 69 +++++++++---------- 4 files changed, 65 insertions(+), 88 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 9f9d21923ff7..d47e4b747e43 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2,6 +2,7 @@ use hir::HasSource; use syntax::{ Edition, ast::{self, AstNode, make}, + ted, }; use crate::{ @@ -157,19 +158,21 @@ fn add_missing_impl_members_inner( &target_scope, ); + let mut editor = edit.make_editor(impl_def.syntax()); if let Some(cap) = ctx.config.snippet_cap { let mut placeholder = None; if let DefaultMethods::No = mode { if let ast::AssocItem::Fn(func) = &first_new_item { - if try_gen_trait_body( + if let Some(body) = try_gen_trait_body( ctx, func, trait_ref, &impl_def, target_scope.krate().edition(ctx.sema.db), - ) - .is_none() + ) && let Some(func_body) = func.body() { + ted::replace(func_body.syntax(), body.syntax()); + } else { if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { if m.syntax().text() == "todo!()" { @@ -195,7 +198,7 @@ fn try_gen_trait_body( trait_ref: hir::TraitRef<'_>, impl_def: &ast::Impl, edition: Edition, -) -> Option<()> { +) -> Option { let trait_path = make::ext::ident_path( &trait_ref.trait_().name(ctx.db()).display(ctx.db(), edition).to_string(), ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 806c8fba9ea4..b7b8ae1d9c06 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -135,6 +135,7 @@ fn add_assist( &annotated_name, trait_, replace_trait_path, + impl_is_unsafe, ); update_attribute(builder, old_derives, old_tree, old_trait_path, attr); @@ -142,13 +143,7 @@ fn add_assist( match (ctx.config.snippet_cap, impl_def_with_items) { (None, None) => { - let impl_def = generate_trait_impl(adt, trait_path); - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } + let impl_def = generate_trait_impl(impl_is_unsafe, adt, trait_path); ted::insert_all( insert_after, @@ -156,26 +151,13 @@ fn add_assist( ); } (None, Some((impl_def, _))) => { - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } ted::insert_all( insert_after, vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], ); } (Some(cap), None) => { - let impl_def = generate_trait_impl(adt, trait_path); - - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } + let impl_def = generate_trait_impl(impl_is_unsafe, adt, trait_path); if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) { @@ -188,26 +170,13 @@ fn add_assist( ); } (Some(cap), Some((impl_def, first_assoc_item))) => { - let mut added_snippet = false; - - if impl_is_unsafe { - ted::insert( - Position::first_child_of(impl_def.syntax()), - make::token(T![unsafe]), - ); - } - - if let ast::AssocItem::Fn(ref func) = first_assoc_item { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) { - if m.syntax().text() == "todo!()" { - // Make the `todo!()` a placeholder - builder.add_placeholder_snippet(cap, m); - added_snippet = true; - } - } - } - - if !added_snippet { + if let ast::AssocItem::Fn(ref func) = first_assoc_item + && let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) + && m.syntax().text() == "todo!()" + { + // Make the `todo!()` a placeholder + builder.add_placeholder_snippet(cap, m); + } else { // If we haven't already added a snippet, add a tabstop before the generated function builder.add_tabstop_before(cap, first_assoc_item); } @@ -228,6 +197,7 @@ fn impl_def_from_trait( annotated_name: &ast::Name, trait_: Option, trait_path: &ast::Path, + impl_is_unsafe: bool, ) -> Option<(ast::Impl, ast::AssocItem)> { let trait_ = trait_?; let target_scope = sema.scope(annotated_name.syntax())?; @@ -245,14 +215,18 @@ fn impl_def_from_trait( if trait_items.is_empty() { return None; } - let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone())); + let impl_def = generate_trait_impl(impl_is_unsafe, adt, make::ty_path(trait_path.clone())); let first_assoc_item = add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope); // Generate a default `impl` function body for the derived trait. if let ast::AssocItem::Fn(ref func) = first_assoc_item { - let _ = gen_trait_fn_body(func, trait_path, adt, None); + if let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) + && let Some(func_body) = func.body() + { + ted::replace(func_body.syntax(), body.syntax()); + } }; Some((impl_def, first_assoc_item)) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 31649a71b9a2..bc3fdf10d7a2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -668,19 +668,19 @@ pub(crate) fn generate_impl_with_item( adt: &ast::Adt, body: Option, ) -> ast::Impl { - generate_impl_inner(adt, None, true, body) + generate_impl_inner(false, adt, None, true, body) } pub(crate) fn generate_impl(adt: &ast::Adt) -> ast::Impl { - generate_impl_inner(adt, None, true, None) + generate_impl_inner(false, adt, None, true, None) } /// Generates the corresponding `impl for Type {}` including type /// and lifetime parameters, with `` appended to `impl`'s generic parameters' bounds. /// /// This is useful for traits like `PartialEq`, since `impl PartialEq for U` often requires `T: PartialEq`. -pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), true, None) +pub(crate) fn generate_trait_impl(is_unsafe: bool, adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { + generate_impl_inner(is_unsafe, adt, Some(trait_), true, None) } /// Generates the corresponding `impl for Type {}` including type @@ -688,10 +688,11 @@ pub(crate) fn generate_trait_impl(adt: &ast::Adt, trait_: ast::Type) -> ast::Imp /// /// This is useful for traits like `From`, since `impl From for U` doesn't require `T: From`. pub(crate) fn generate_trait_impl_intransitive(adt: &ast::Adt, trait_: ast::Type) -> ast::Impl { - generate_impl_inner(adt, Some(trait_), false, None) + generate_impl_inner(false, adt, Some(trait_), false, None) } fn generate_impl_inner( + is_unsafe: bool, adt: &ast::Adt, trait_: Option, trait_is_transitive: bool, @@ -735,7 +736,7 @@ fn generate_impl_inner( let impl_ = match trait_ { Some(trait_) => make::impl_trait( - false, + is_unsafe, None, None, generic_params, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 026209efc446..87e90e85193c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -1,10 +1,7 @@ //! This module contains functions to generate default trait impl function bodies where possible. use hir::TraitRef; -use syntax::{ - ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make}, - ted, -}; +use syntax::ast::{self, AstNode, BinaryOp, CmpOp, HasName, LogicOp, edit::AstNodeEdit, make}; /// Generate custom trait bodies without default implementation where possible. /// @@ -18,21 +15,33 @@ pub(crate) fn gen_trait_fn_body( trait_path: &ast::Path, adt: &ast::Adt, trait_ref: Option>, -) -> Option<()> { +) -> Option { + let _ = func.body()?; match trait_path.segment()?.name_ref()?.text().as_str() { - "Clone" => gen_clone_impl(adt, func), - "Debug" => gen_debug_impl(adt, func), - "Default" => gen_default_impl(adt, func), - "Hash" => gen_hash_impl(adt, func), - "PartialEq" => gen_partial_eq(adt, func, trait_ref), - "PartialOrd" => gen_partial_ord(adt, func, trait_ref), + "Clone" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); + gen_clone_impl(adt) + } + "Debug" => gen_debug_impl(adt), + "Default" => gen_default_impl(adt), + "Hash" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); + gen_hash_impl(adt) + } + "PartialEq" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); + gen_partial_eq(adt, trait_ref) + } + "PartialOrd" => { + stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); + gen_partial_ord(adt, trait_ref) + } _ => None, } } /// Generate a `Clone` impl based on the fields and members of the target type. -fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); +fn gen_clone_impl(adt: &ast::Adt) -> Option { fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)).into() @@ -139,12 +148,11 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } /// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { +fn gen_debug_impl(adt: &ast::Adt) -> Option { let annotated_name = adt.name()?; match adt { // `Debug` cannot be derived for unions, so no default impl can be provided. @@ -248,8 +256,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let body = make::block_expr(None, Some(match_expr.into())); let body = body.indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } ast::Adt::Struct(strukt) => { @@ -296,14 +303,13 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let method = make::name_ref("finish"); let expr = make::expr_method_call(expr, method, make::arg_list(None)).into(); let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } } } /// Generate a `Debug` impl based on the fields and members of the target type. -fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { +fn gen_default_impl(adt: &ast::Adt) -> Option { fn gen_default_call() -> Option { let fn_name = make::ext::path_from_idents(["Default", "default"])?; Some(make::expr_call(make::expr_path(fn_name), make::arg_list(None)).into()) @@ -342,15 +348,13 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } }; let body = make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)); - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } } } /// Generate a `Hash` impl based on the fields and members of the target type. -fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "hash")); +fn gen_hash_impl(adt: &ast::Adt) -> Option { fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -400,13 +404,11 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { }, }; - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } /// Generate a `PartialEq` impl based on the fields and members of the target type. -fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option>) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "eq")); +fn gen_partial_eq(adt: &ast::Adt, trait_ref: Option>) -> Option { fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -595,12 +597,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option }, }; - ted::replace(func.body()?.syntax(), body.syntax()); - Some(()) + Some(body) } -fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option>) -> Option<()> { - stdx::always!(func.name().is_some_and(|name| name.text() == "partial_cmp")); +fn gen_partial_ord(adt: &ast::Adt, trait_ref: Option>) -> Option { fn gen_partial_eq_match(match_target: ast::Expr) -> Option { let mut arms = vec![]; @@ -686,8 +686,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn, trait_ref: Option Option { From 225988b762899ab5e93c8ad028c080fd5c257c84 Mon Sep 17 00:00:00 2001 From: Hmikihiro <34ttrweoewiwe28@gmail.com> Date: Wed, 23 Jul 2025 04:28:49 +0900 Subject: [PATCH 053/201] migrate_replace_derive_with_manual_impl --- .../src/handlers/add_missing_impl_members.rs | 13 +-- .../replace_derive_with_manual_impl.rs | 92 ++++++++----------- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index d47e4b747e43..7459f37be188 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -158,7 +158,6 @@ fn add_missing_impl_members_inner( &target_scope, ); - let mut editor = edit.make_editor(impl_def.syntax()); if let Some(cap) = ctx.config.snippet_cap { let mut placeholder = None; if let DefaultMethods::No = mode { @@ -172,13 +171,11 @@ fn add_missing_impl_members_inner( ) && let Some(func_body) = func.body() { ted::replace(func_body.syntax(), body.syntax()); - } else { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) - { - if m.syntax().text() == "todo!()" { - placeholder = Some(m); - } - } + } else if let Some(m) = + func.syntax().descendants().find_map(ast::MacroCall::cast) + && m.syntax().text() == "todo!()" + { + placeholder = Some(m); } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index b7b8ae1d9c06..2f979b7ee07b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -5,12 +5,12 @@ use syntax::{ SyntaxKind::WHITESPACE, T, ast::{self, AstNode, HasName, make}, - ted::{self, Position}, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ AssistConfig, AssistId, - assist_context::{AssistContext, Assists, SourceChangeBuilder}, + assist_context::{AssistContext, Assists}, utils::{ DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, generate_trait_impl, @@ -126,9 +126,9 @@ fn add_assist( let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| { - let insert_after = ted::Position::after(builder.make_mut(adt.clone()).syntax()); + let insert_after = Position::after(adt.syntax()); let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false); - let impl_def_with_items = impl_def_from_trait( + let impl_def = impl_def_from_trait( &ctx.sema, ctx.config, adt, @@ -137,39 +137,23 @@ fn add_assist( replace_trait_path, impl_is_unsafe, ); - update_attribute(builder, old_derives, old_tree, old_trait_path, attr); + + let mut editor = builder.make_editor(attr.syntax()); + update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr); let trait_path = make::ty_path(replace_trait_path.clone()); - match (ctx.config.snippet_cap, impl_def_with_items) { - (None, None) => { - let impl_def = generate_trait_impl(impl_is_unsafe, adt, trait_path); + let (impl_def, first_assoc_item) = if let Some(impl_def) = impl_def { + ( + impl_def.clone(), + impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()), + ) + } else { + (generate_trait_impl(impl_is_unsafe, adt, trait_path), None) + }; - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (None, Some((impl_def, _))) => { - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (Some(cap), None) => { - let impl_def = generate_trait_impl(impl_is_unsafe, adt, trait_path); - - if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) - { - builder.add_tabstop_after_token(cap, l_curly); - } - - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); - } - (Some(cap), Some((impl_def, first_assoc_item))) => { + if let Some(cap) = ctx.config.snippet_cap { + if let Some(first_assoc_item) = first_assoc_item { if let ast::AssocItem::Fn(ref func) = first_assoc_item && let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) && m.syntax().text() == "todo!()" @@ -180,13 +164,18 @@ fn add_assist( // If we haven't already added a snippet, add a tabstop before the generated function builder.add_tabstop_before(cap, first_assoc_item); } - - ted::insert_all( - insert_after, - vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], - ); + } else if let Some(l_curly) = + impl_def.assoc_item_list().and_then(|it| it.l_curly_token()) + { + builder.add_tabstop_after_token(cap, l_curly); } - }; + } + + editor.insert_all( + insert_after, + vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()], + ); + builder.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -198,7 +187,7 @@ fn impl_def_from_trait( trait_: Option, trait_path: &ast::Path, impl_is_unsafe: bool, -) -> Option<(ast::Impl, ast::AssocItem)> { +) -> Option { let trait_ = trait_?; let target_scope = sema.scope(annotated_name.syntax())?; @@ -217,23 +206,25 @@ fn impl_def_from_trait( } let impl_def = generate_trait_impl(impl_is_unsafe, adt, make::ty_path(trait_path.clone())); - let first_assoc_item = + let _ = add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope); - + let impl_def = impl_def.clone_subtree(); + let mut editor = SyntaxEditor::new(impl_def.syntax().clone()); + let first_assoc_item = impl_def.assoc_item_list().and_then(|item| item.assoc_items().next())?; // Generate a default `impl` function body for the derived trait. if let ast::AssocItem::Fn(ref func) = first_assoc_item { if let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) && let Some(func_body) = func.body() { - ted::replace(func_body.syntax(), body.syntax()); + editor.replace(func_body.syntax(), body.syntax()); } }; - - Some((impl_def, first_assoc_item)) + let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?; + Some(impl_def) } fn update_attribute( - builder: &mut SourceChangeBuilder, + editor: &mut SyntaxEditor, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, @@ -246,8 +237,6 @@ fn update_attribute( let has_more_derives = !new_derives.is_empty(); if has_more_derives { - let old_tree = builder.make_mut(old_tree.clone()); - // Make the paths into flat lists of tokens in a vec let tt = new_derives.iter().map(|path| path.syntax().clone()).map(|node| { node.descendants_with_tokens() @@ -262,18 +251,17 @@ fn update_attribute( let tt = tt.collect::>(); let new_tree = make::token_tree(T!['('], tt).clone_for_update(); - ted::replace(old_tree.syntax(), new_tree.syntax()); + editor.replace(old_tree.syntax(), new_tree.syntax()); } else { // Remove the attr and any trailing whitespace - let attr = builder.make_mut(attr.clone()); if let Some(line_break) = attr.syntax().next_sibling_or_token().filter(|t| t.kind() == WHITESPACE) { - ted::remove(line_break) + editor.delete(line_break) } - ted::remove(attr.syntax()) + editor.delete(attr.syntax()) } } From 27e2709f3e8d8f03b05704bc7e3e9110dd64397b Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:05:03 +0000 Subject: [PATCH 054/201] Improve coordinator channel handling Remove usage of Any, reduce visibility of fields and remove unused backend arguments. --- compiler/rustc_codegen_ssa/src/back/write.rs | 49 +++++++++----------- compiler/rustc_codegen_ssa/src/base.rs | 16 ++----- 2 files changed, 25 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 7be274df1d41..a41cbd306d0c 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::assert_matches::assert_matches; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -372,8 +371,6 @@ pub struct CodegenContext { /// The incremental compilation session directory, or None if we are not /// compiling incrementally pub incr_comp_session_dir: Option, - /// Channel back to the main control thread to send messages to - pub coordinator_send: Sender>, /// `true` if the codegen should be run in parallel. /// /// Depends on [`ExtraBackendMethods::supports_parallel()`] and `-Zno_parallel_backend`. @@ -1122,10 +1119,10 @@ fn start_executing_work( autodiff_items: &[AutoDiffItem], shared_emitter: SharedEmitter, codegen_worker_send: Sender, - coordinator_receive: Receiver>, + coordinator_receive: Receiver>, regular_config: Arc, allocator_config: Arc, - tx_to_llvm_workers: Sender>, + tx_to_llvm_workers: Sender>, ) -> thread::JoinHandle> { let coordinator_send = tx_to_llvm_workers; let sess = tcx.sess; @@ -1153,7 +1150,7 @@ fn start_executing_work( let coordinator_send2 = coordinator_send.clone(); let helper = jobserver::client() .into_helper_thread(move |token| { - drop(coordinator_send2.send(Box::new(Message::Token::(token)))); + drop(coordinator_send2.send(Message::Token::(token))); }) .expect("failed to spawn helper thread"); @@ -1187,7 +1184,6 @@ fn start_executing_work( remark: sess.opts.cg.remark.clone(), remark_dir, incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - coordinator_send, expanded_args: tcx.sess.expanded_args.clone(), diag_emitter: shared_emitter.clone(), output_filenames: Arc::clone(tcx.output_filenames(())), @@ -1423,7 +1419,7 @@ fn start_executing_work( let (item, _) = work_items.pop().expect("queue empty - queue_full_enough() broken?"); main_thread_state = MainThreadState::Lending; - spawn_work(&cgcx, &mut llvm_start_time, item); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); } } } else if codegen_state == Completed { @@ -1502,7 +1498,7 @@ fn start_executing_work( MainThreadState::Idle => { if let Some((item, _)) = work_items.pop() { main_thread_state = MainThreadState::Lending; - spawn_work(&cgcx, &mut llvm_start_time, item); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); } else { // There is no unstarted work, so let the main thread // take over for a running worker. Otherwise the @@ -1538,7 +1534,7 @@ fn start_executing_work( while running_with_own_token < tokens.len() && let Some((item, _)) = work_items.pop() { - spawn_work(&cgcx, &mut llvm_start_time, item); + spawn_work(&cgcx, coordinator_send.clone(), &mut llvm_start_time, item); running_with_own_token += 1; } } @@ -1546,8 +1542,7 @@ fn start_executing_work( // Relinquish accidentally acquired extra tokens. tokens.truncate(running_with_own_token); - let msg = coordinator_receive.recv().unwrap(); - match *msg.downcast::>().ok().unwrap() { + match coordinator_receive.recv().unwrap() { // Save the token locally and the next turn of the loop will use // this to spawn a new unit of work, or it may get dropped // immediately if we have no more work to spawn. @@ -1769,6 +1764,7 @@ pub(crate) struct WorkerFatalError; fn spawn_work<'a, B: ExtraBackendMethods>( cgcx: &'a CodegenContext, + coordinator_send: Sender>, llvm_start_time: &mut Option>, work: WorkItem, ) { @@ -1782,7 +1778,7 @@ fn spawn_work<'a, B: ExtraBackendMethods>( // Set up a destructor which will fire off a message that we're done as // we exit. struct Bomb { - coordinator_send: Sender>, + coordinator_send: Sender>, result: Option, FatalError>>, } impl Drop for Bomb { @@ -1794,11 +1790,11 @@ fn spawn_work<'a, B: ExtraBackendMethods>( } None => Message::WorkItem:: { result: Err(None) }, }; - drop(self.coordinator_send.send(Box::new(msg))); + drop(self.coordinator_send.send(msg)); } } - let mut bomb = Bomb:: { coordinator_send: cgcx.coordinator_send.clone(), result: None }; + let mut bomb = Bomb:: { coordinator_send, result: None }; // Execute the work itself, and if it finishes successfully then flag // ourselves as a success as well. @@ -2003,7 +1999,7 @@ impl SharedEmitterMain { } pub struct Coordinator { - pub sender: Sender>, + sender: Sender>, future: Option>>, // Only used for the Message type. phantom: PhantomData, @@ -2020,7 +2016,7 @@ impl Drop for Coordinator { if let Some(future) = self.future.take() { // If we haven't joined yet, signal to the coordinator that it should spawn no more // work, and wait for worker threads to finish. - drop(self.sender.send(Box::new(Message::CodegenAborted::))); + drop(self.sender.send(Message::CodegenAborted::)); drop(future.join()); } } @@ -2079,7 +2075,7 @@ impl OngoingCodegen { pub(crate) fn codegen_finished(&self, tcx: TyCtxt<'_>) { self.wait_for_signal_to_codegen_item(); self.check_for_errors(tcx.sess); - drop(self.coordinator.sender.send(Box::new(Message::CodegenComplete::))); + drop(self.coordinator.sender.send(Message::CodegenComplete::)); } pub(crate) fn check_for_errors(&self, sess: &Session) { @@ -2100,28 +2096,25 @@ impl OngoingCodegen { } pub(crate) fn submit_codegened_module_to_llvm( - _backend: &B, - tx_to_llvm_workers: &Sender>, + coordinator: &Coordinator, module: ModuleCodegen, cost: u64, ) { let llvm_work_item = WorkItem::Optimize(module); - drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone:: { llvm_work_item, cost }))); + drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost })); } pub(crate) fn submit_post_lto_module_to_llvm( - _backend: &B, - tx_to_llvm_workers: &Sender>, + coordinator: &Coordinator, module: CachedModuleCodegen, ) { let llvm_work_item = WorkItem::CopyPostLtoArtifacts(module); - drop(tx_to_llvm_workers.send(Box::new(Message::CodegenDone:: { llvm_work_item, cost: 0 }))); + drop(coordinator.sender.send(Message::CodegenDone:: { llvm_work_item, cost: 0 })); } pub(crate) fn submit_pre_lto_module_to_llvm( - _backend: &B, tcx: TyCtxt<'_>, - tx_to_llvm_workers: &Sender>, + coordinator: &Coordinator, module: CachedModuleCodegen, ) { let filename = pre_lto_bitcode_filename(&module.name); @@ -2135,10 +2128,10 @@ pub(crate) fn submit_pre_lto_module_to_llvm( }) }; // Schedule the module to be loaded - drop(tx_to_llvm_workers.send(Box::new(Message::AddImportOnlyModule:: { + drop(coordinator.sender.send(Message::AddImportOnlyModule:: { module_data: SerializedModule::FromUncompressedFile(mmap), work_product: module.source, - }))); + })); } fn pre_lto_bitcode_filename(module_name: &str) -> String { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 833456abb8ab..a5807c56e317 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -702,8 +702,7 @@ pub fn codegen_crate( // These modules are generally cheap and won't throw off scheduling. let cost = 0; submit_codegened_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, ModuleCodegen::new_allocator(llmod_id, module_llvm), cost, ); @@ -800,18 +799,12 @@ pub fn codegen_crate( // compilation hang on post-monomorphization errors. tcx.dcx().abort_if_errors(); - submit_codegened_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, - module, - cost, - ); + submit_codegened_module_to_llvm(&ongoing_codegen.coordinator, module, cost); } CguReuse::PreLto => { submit_pre_lto_module_to_llvm( - &backend, tcx, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), @@ -820,8 +813,7 @@ pub fn codegen_crate( } CguReuse::PostLto => { submit_post_lto_module_to_llvm( - &backend, - &ongoing_codegen.coordinator.sender, + &ongoing_codegen.coordinator, CachedModuleCodegen { name: cgu.name().to_string(), source: cgu.previous_work_product(tcx), From fe2eeabe27ce3d5b871ab903e65b4707ad015764 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 25 Jul 2025 09:42:18 +0000 Subject: [PATCH 055/201] Use the object crate rather than LLVM for extracting bitcode sections --- compiler/rustc_codegen_llvm/messages.ftl | 2 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 31 +++++------------ compiler/rustc_codegen_llvm/src/errors.rs | 2 +- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 7 ---- .../rustc_llvm/llvm-wrapper/PassWrapper.cpp | 34 ------------------- 5 files changed, 10 insertions(+), 66 deletions(-) diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl index 3d5f17a60345..ce9a51b539d8 100644 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ b/compiler/rustc_codegen_llvm/messages.ftl @@ -12,7 +12,7 @@ codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_ codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} -codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$llvm_err}) +codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err}) codegen_llvm_mismatch_data_layout = data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 767835c34f02..cac7d49b74a5 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use std::{io, iter, slice}; use object::read::archive::ArchiveFile; +use object::{Object, ObjectSection}; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput}; use rustc_codegen_ssa::traits::*; @@ -105,31 +106,15 @@ fn get_bitcode_slice_from_object_data<'a>( // name" which in the public API for sections gets treated as part of the section name, but // internally in MachOObjectFile.cpp gets treated separately. let section_name = bitcode_section_name(cgcx).to_str().unwrap().trim_start_matches("__LLVM,"); - let mut len = 0; - let data = unsafe { - llvm::LLVMRustGetSliceFromObjectDataByName( - obj.as_ptr(), - obj.len(), - section_name.as_ptr(), - section_name.len(), - &mut len, - ) - }; - if !data.is_null() { - assert!(len != 0); - let bc = unsafe { slice::from_raw_parts(data, len) }; - // `bc` must be a sub-slice of `obj`. - assert!(obj.as_ptr() <= bc.as_ptr()); - assert!(bc[bc.len()..bc.len()].as_ptr() <= obj[obj.len()..obj.len()].as_ptr()); + let obj = + object::File::parse(obj).map_err(|err| LtoBitcodeFromRlib { err: err.to_string() })?; - Ok(bc) - } else { - assert!(len == 0); - Err(LtoBitcodeFromRlib { - llvm_err: llvm::last_error().unwrap_or_else(|| "unknown LLVM error".to_string()), - }) - } + let section = obj + .section_by_name(section_name) + .ok_or_else(|| LtoBitcodeFromRlib { err: format!("Can't find section {section_name}") })?; + + section.data().map_err(|err| LtoBitcodeFromRlib { err: err.to_string() }) } /// Performs fat LTO by merging all modules into a single one and returning it diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 2a889888a39b..627b0c9ff3b3 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -39,7 +39,7 @@ pub(crate) struct AutoDiffWithoutEnable; #[derive(Diagnostic)] #[diag(codegen_llvm_lto_bitcode_from_rlib)] pub(crate) struct LtoBitcodeFromRlib { - pub llvm_err: String, + pub err: String, } #[derive(Diagnostic)] diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index edfb29dd1be7..0d0cb5f139ee 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -2612,13 +2612,6 @@ unsafe extern "C" { len: usize, Identifier: *const c_char, ) -> Option<&Module>; - pub(crate) fn LLVMRustGetSliceFromObjectDataByName( - data: *const u8, - len: usize, - name: *const u8, - name_len: usize, - out_len: &mut usize, - ) -> *const u8; pub(crate) fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>; pub(crate) fn LLVMRustLinkerAdd( diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index a2e4d7306cbf..8c34052770e6 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -1650,40 +1650,6 @@ extern "C" LLVMModuleRef LLVMRustParseBitcodeForLTO(LLVMContextRef Context, return wrap(std::move(*SrcOrError).release()); } -// Find a section of an object file by name. Fail if the section is missing or -// empty. -extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data, - size_t len, - const char *name, - size_t name_len, - size_t *out_len) { - *out_len = 0; - auto Name = StringRef(name, name_len); - auto Data = StringRef(data, len); - auto Buffer = MemoryBufferRef(Data, ""); // The id is unused. - file_magic Type = identify_magic(Buffer.getBuffer()); - Expected> ObjFileOrError = - object::ObjectFile::createObjectFile(Buffer, Type); - if (!ObjFileOrError) { - LLVMRustSetLastError(toString(ObjFileOrError.takeError()).c_str()); - return nullptr; - } - for (const object::SectionRef &Sec : (*ObjFileOrError)->sections()) { - Expected SecName = Sec.getName(); - if (SecName && *SecName == Name) { - Expected SectionOrError = Sec.getContents(); - if (!SectionOrError) { - LLVMRustSetLastError(toString(SectionOrError.takeError()).c_str()); - return nullptr; - } - *out_len = SectionOrError->size(); - return SectionOrError->data(); - } - } - LLVMRustSetLastError("could not find requested section"); - return nullptr; -} - // Computes the LTO cache key for the provided 'ModId' in the given 'Data', // storing the result in 'KeyOut'. // Currently, this cache key is a SHA-1 hash of anything that could affect From 0b323eacd4c4cf99d18bd75ad02b2139dd990297 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 24 Jul 2025 14:53:13 +0000 Subject: [PATCH 056/201] uniquify root goals during HIR typeck --- .../rustc_hir_typeck/src/typeck_root_ctxt.rs | 7 +- compiler/rustc_infer/src/infer/at.rs | 2 + compiler/rustc_infer/src/infer/context.rs | 4 ++ compiler/rustc_infer/src/infer/mod.rs | 38 +++++++++- .../src/canonicalizer.rs | 72 +++++++++++++------ .../src/solve/eval_ctxt/canonical.rs | 2 + .../src/solve/eval_ctxt/mod.rs | 5 +- .../rustc_next_trait_solver/src/solve/mod.rs | 2 - compiler/rustc_type_ir/src/infer_ctxt.rs | 4 ++ tests/crashes/139409.rs | 12 ---- ...iguity-due-to-uniquification-1.next.stderr | 19 +++++ .../ambiguity-due-to-uniquification-1.rs | 17 +++++ ...iguity-due-to-uniquification-2.next.stderr | 17 +++++ .../ambiguity-due-to-uniquification-2.rs | 20 ++++++ 14 files changed, 180 insertions(+), 41 deletions(-) delete mode 100644 tests/crashes/139409.rs create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 9f4ab8ca5d41..20844dee7932 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> { pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self { let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner; - let infcx = - tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id)); + let infcx = tcx + .infer_ctxt() + .ignoring_regions() + .in_hir_typeck() + .build(TypingMode::typeck_for_body(tcx, def_id)); let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner)); let fulfillment_cx = RefCell::new(>::new(&infcx)); diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 5fe795bd23a1..ad19cdef4e75 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode: self.typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), @@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> { tcx: self.tcx, typing_mode, considering_regions: self.considering_regions, + in_hir_typeck: self.in_hir_typeck, skip_leak_check: self.skip_leak_check, inner: self.inner.clone(), lexical_region_resolutions: self.lexical_region_resolutions.clone(), diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs index bb9c88500936..21e999b080d5 100644 --- a/compiler/rustc_infer/src/infer/context.rs +++ b/compiler/rustc_infer/src/infer/context.rs @@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { self.next_trait_solver } + fn in_hir_typeck(&self) -> bool { + self.in_hir_typeck + } + fn typing_mode(&self) -> ty::TypingMode<'tcx> { self.typing_mode() } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 2d269e320b64..17c587c5e7fd 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -244,9 +244,28 @@ pub struct InferCtxt<'tcx> { typing_mode: TypingMode<'tcx>, /// Whether this inference context should care about region obligations in - /// the root universe. Most notably, this is used during hir typeck as region + /// the root universe. Most notably, this is used during HIR typeck as region /// solving is left to borrowck instead. pub considering_regions: bool, + /// Whether this inference context is used by HIR typeck. If so, we uniquify regions + /// with `-Znext-solver`. This is necessary as borrowck will start by replacing each + /// occurance of a free region with a unique inference variable so if HIR typeck + /// ends up depending on two regions being equal we'd get unexpected mismatches + /// between HIR typeck and MIR typeck, resulting in an ICE. + /// + /// The trait solver sometimes depends on regions being identical. As a concrete example + /// the trait solver ignores other candidates if one candidate exists without any constraints. + /// The goal `&'a u32: Equals<&'a u32>` has no constraints right now, but if we replace + /// each occurance of `'a` with a unique region the goal now equates these regions. + /// + /// See the tests in trait-system-refactor-initiative#27 for concrete examples. + /// + /// FIXME(-Znext-solver): This is insufficient in theory as a goal `T: Trait` + /// may rely on the two occurances of `?x` being identical. If `?x` gets inferred to a + /// type containing regions, this will no longer be the case. We can handle this case + /// by storing goals which hold while still depending on inference vars and then + /// reproving them before writeback. + pub in_hir_typeck: bool, /// If set, this flag causes us to skip the 'leak check' during /// higher-ranked subtyping operations. This flag is a temporary one used @@ -506,6 +525,7 @@ pub struct TypeOutlivesConstraint<'tcx> { pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, considering_regions: bool, + in_hir_typeck: bool, skip_leak_check: bool, /// Whether we should use the new trait solver in the local inference context, /// which affects things like which solver is used in `predicate_may_hold`. @@ -518,6 +538,7 @@ impl<'tcx> TyCtxt<'tcx> { InferCtxtBuilder { tcx: self, considering_regions: true, + in_hir_typeck: false, skip_leak_check: false, next_trait_solver: self.next_trait_solver_globally(), } @@ -535,6 +556,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } + pub fn in_hir_typeck(mut self) -> Self { + self.in_hir_typeck = true; + self + } + pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self { self.skip_leak_check = skip_leak_check; self @@ -568,12 +594,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> { - let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } = - *self; + let InferCtxtBuilder { + tcx, + considering_regions, + in_hir_typeck, + skip_leak_check, + next_trait_solver, + } = *self; InferCtxt { tcx, typing_mode, considering_regions, + in_hir_typeck, skip_leak_check, inner: RefCell::new(InferCtxtInner::new()), lexical_region_resolutions: RefCell::new(None), diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index a418aa82100c..1bc35e599c70 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -19,6 +19,20 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( ) .unwrap(); +#[derive(Debug, Clone, Copy)] +enum CanonicalizeInputKind { + /// When canonicalizing the `param_env`, we keep `'static` as merging + /// trait candidates relies on it when deciding whether a where-bound + /// is trivial. + ParamEnv, + /// When canonicalizing predicates, we don't keep `'static`. If we're + /// currently outside of the trait solver and canonicalize the root goal + /// during HIR typeck, we replace each occurance of a region with a + /// unique region variable. See the comment on `InferCtxt::in_hir_typeck` + /// for more details. + Predicate { is_hir_typeck_root_goal: bool }, +} + /// Whether we're canonicalizing a query input or the query response. /// /// When canonicalizing an input we're in the context of the caller @@ -26,10 +40,7 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits( /// query. #[derive(Debug, Clone, Copy)] enum CanonicalizeMode { - /// When canonicalizing the `param_env`, we keep `'static` as merging - /// trait candidates relies on it when deciding whether a where-bound - /// is trivial. - Input { keep_static: bool }, + Input(CanonicalizeInputKind), /// FIXME: We currently return region constraints referring to /// placeholders and inference variables from a binder instantiated /// inside of the query. @@ -122,7 +133,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { let mut variables = Vec::new(); let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables: &mut variables, variable_lookup_table: Default::default(), @@ -154,7 +165,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } else { let mut env_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: true }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv), variables, variable_lookup_table: Default::default(), @@ -180,6 +191,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { pub fn canonicalize_input>( delegate: &'a D, variables: &'a mut Vec, + is_hir_typeck_root_goal: bool, input: QueryInput, ) -> ty::Canonical> { // First canonicalize the `param_env` while keeping `'static` @@ -189,7 +201,9 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { // while *mostly* reusing the canonicalizer from above. let mut rest_canonicalizer = Canonicalizer { delegate, - canonicalize_mode: CanonicalizeMode::Input { keep_static: false }, + canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal, + }), variables, variable_lookup_table, @@ -296,7 +310,7 @@ impl<'a, D: SolverDelegate, I: Interner> Canonicalizer<'a, D, I> { } } - fn cached_fold_ty(&mut self, t: I::Ty) -> I::Ty { + fn inner_fold_ty(&mut self, t: I::Ty) -> I::Ty { let kind = match t.kind() { ty::Infer(i) => match i { ty::TyVar(vid) => { @@ -413,10 +427,10 @@ impl, I: Interner> TypeFolder for Canonicaliz // We don't canonicalize `ReStatic` in the `param_env` as we use it // when checking whether a `ParamEnv` candidate is global. ty::ReStatic => match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { CanonicalVarKind::Region(ty::UniverseIndex::ROOT) } - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { .. } => return r, }, @@ -428,12 +442,12 @@ impl, I: Interner> TypeFolder for Canonicaliz // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode { - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { panic!("unexpected region in response: {r:?}") } @@ -441,7 +455,7 @@ impl, I: Interner> TypeFolder for Canonicaliz ty::RePlaceholder(placeholder) => match self.canonicalize_mode { // We canonicalize placeholder regions as existentials in query inputs. - CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { max_input_universe } => { // If we have a placeholder region inside of a query, it must be from // a new universe. @@ -459,9 +473,7 @@ impl, I: Interner> TypeFolder for Canonicaliz "region vid should have been resolved fully before canonicalization" ); match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: _ } => { - CanonicalVarKind::Region(ty::UniverseIndex::ROOT) - } + CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => { CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap()) } @@ -469,16 +481,34 @@ impl, I: Interner> TypeFolder for Canonicaliz } }; - let var = self.get_or_insert_bound_var(r, kind); + let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + let var = ty::BoundVar::from(self.variables.len()); + self.variables.push(r.into()); + self.var_kinds.push(kind); + var + } else { + self.get_or_insert_bound_var(r, kind) + }; Region::new_anon_bound(self.cx(), self.binder_index, var) } fn fold_ty(&mut self, t: I::Ty) -> I::Ty { - if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { + if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { + is_hir_typeck_root_goal: true, + }) = self.canonicalize_mode + { + // If we're canonicalizing a root goal during HIR typeck, we + // must not use the `cache` as we want to map each occurrence + // of a region to a unique existential variable. + self.inner_fold_ty(t) + } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) { ty } else { - let res = self.cached_fold_ty(t); + let res = self.inner_fold_ty(t); let old = self.cache.insert((self.binder_index, t), res); assert_eq!(old, None); res @@ -541,9 +571,9 @@ impl, I: Interner> TypeFolder for Canonicaliz fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses { match self.canonicalize_mode { - CanonicalizeMode::Input { keep_static: true } + CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv) | CanonicalizeMode::Response { max_input_universe: _ } => {} - CanonicalizeMode::Input { keep_static: false } => { + CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => { panic!("erasing 'static in env") } } diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 5ed316aa6b13..de1330ca82a4 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -55,6 +55,7 @@ where /// for each bound variable. pub(super) fn canonicalize_goal( &self, + is_hir_typeck_root_goal: bool, goal: Goal, ) -> (Vec, CanonicalInput) { // We only care about one entry per `OpaqueTypeKey` here, @@ -67,6 +68,7 @@ where let canonical = Canonicalizer::canonicalize_input( self.delegate, &mut orig_values, + is_hir_typeck_root_goal, QueryInput { goal, predefined_opaques_in_body: self diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index ce9b794d40d3..053ccf285cf0 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -447,7 +447,10 @@ where )); } - let (orig_values, canonical_goal) = self.canonicalize_goal(goal); + let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) + && self.delegate.in_hir_typeck(); + + let (orig_values, canonical_goal) = self.canonicalize_goal(is_hir_typeck_root_goal, goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 5ea3f0d10617..f39426c7689d 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -252,8 +252,6 @@ where return None; } - // FIXME(-Znext-solver): Add support to merge region constraints in - // responses to deal with trait-system-refactor-initiative#27. let one = responses[0]; if responses[1..].iter().all(|&resp| resp == one) { return Some(one); diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs index e86a2305e233..b4873c8c71cb 100644 --- a/compiler/rustc_type_ir/src/infer_ctxt.rs +++ b/compiler/rustc_type_ir/src/infer_ctxt.rs @@ -148,6 +148,10 @@ pub trait InferCtxtLike: Sized { true } + fn in_hir_typeck(&self) -> bool { + false + } + fn typing_mode(&self) -> TypingMode; fn universe(&self) -> ty::UniverseIndex; diff --git a/tests/crashes/139409.rs b/tests/crashes/139409.rs deleted file mode 100644 index 68cbfa153deb..000000000000 --- a/tests/crashes/139409.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ known-bug: #139409 -//@ compile-flags: -Znext-solver=globally - -fn main() { - trait B {} - impl B for () {} - trait D: B + B { - fn f(&self) {} - } - impl D for () {} - (&() as &dyn D<&(), &()>).f() -} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr new file mode 100644 index 000000000000..141a07b4be7c --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.next.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` + --> $DIR/ambiguity-due-to-uniquification-1.rs:15:31 + | +LL | (&() as &dyn D<&(), &()>).f() + | ^ + | + = note: cannot satisfy `dyn D<&(), &()>: B<&()>` + = help: the trait `B` is implemented for `()` +note: required by a bound in `D::f` + --> $DIR/ambiguity-due-to-uniquification-1.rs:10:16 + | +LL | trait D: B + B { + | ^^^^ required by this bound in `D::f` +LL | fn f(&self) {} + | - required by a bound in this associated function + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs new file mode 100644 index 000000000000..cfdf74046fbd --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-1.rs @@ -0,0 +1,17 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test for #139409 and trait-system-refactor-initiative#27. + +trait B {} +impl B for () {} +trait D: B + B { + fn f(&self) {} +} +impl D for () {} +fn main() { + (&() as &dyn D<&(), &()>).f() + //[next]~^ ERROR type annotations needed: cannot satisfy `dyn D<&(), &()>: B<&()>` +} diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr new file mode 100644 index 000000000000..3b4788899969 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.next.stderr @@ -0,0 +1,17 @@ +error[E0283]: type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + --> $DIR/ambiguity-due-to-uniquification-2.rs:16:23 + | +LL | impls_trait::<'y, _>(foo::<'x, 'y>()); + | ^ + | + = note: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` + = help: the trait `Trait<'t>` is implemented for `()` +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-2.rs:13:23 + | +LL | fn impls_trait<'x, T: Trait<'x>>(_: T) {} + | ^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs new file mode 100644 index 000000000000..2a9a8b80cc06 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-2.rs @@ -0,0 +1,20 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test from trait-system-refactor-initiative#27. + +trait Trait<'t> {} +impl<'t> Trait<'t> for () {} + +fn foo<'x, 'y>() -> impl Trait<'x> + Trait<'y> {} + +fn impls_trait<'x, T: Trait<'x>>(_: T) {} + +fn bar<'x, 'y>() { + impls_trait::<'y, _>(foo::<'x, 'y>()); + //[next]~^ ERROR type annotations needed: cannot satisfy `impl Trait<'x> + Trait<'y>: Trait<'y>` +} + +fn main() {} From 0e1194781342105625511132f8980e7bec9a5677 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Thu, 24 Jul 2025 01:02:55 +0900 Subject: [PATCH 057/201] Modify around add_trait_assoc_items_to_impl to migrate add_missing_impl_members Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../src/handlers/add_missing_impl_members.rs | 101 ++++++++++++--- .../ide-assists/src/handlers/generate_impl.rs | 49 +++++--- .../replace_derive_with_manual_impl.rs | 34 +++-- .../crates/ide-assists/src/utils.rs | 117 +++++++++--------- .../crates/syntax/src/ast/make.rs | 2 +- .../crates/syntax/src/syntax_editor/edits.rs | 36 ++++++ 6 files changed, 229 insertions(+), 110 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 7459f37be188..ab183ac70895 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2,7 +2,7 @@ use hir::HasSource; use syntax::{ Edition, ast::{self, AstNode, make}, - ted, + syntax_editor::{Position, SyntaxEditor}, }; use crate::{ @@ -148,31 +148,62 @@ fn add_missing_impl_members_inner( let target = impl_def.syntax().text_range(); acc.add(AssistId::quick_fix(assist_id), label, target, |edit| { - let new_impl_def = edit.make_mut(impl_def.clone()); - let first_new_item = add_trait_assoc_items_to_impl( + let new_item = add_trait_assoc_items_to_impl( &ctx.sema, ctx.config, &missing_items, trait_, - &new_impl_def, + &impl_def, &target_scope, ); + let Some((first_new_item, other_items)) = new_item.split_first() else { + return; + }; + + let mut first_new_item = if let DefaultMethods::No = mode + && let ast::AssocItem::Fn(func) = &first_new_item + && let Some(body) = try_gen_trait_body( + ctx, + func, + trait_ref, + &impl_def, + target_scope.krate().edition(ctx.sema.db), + ) + && let Some(func_body) = func.body() + { + let mut func_editor = SyntaxEditor::new(first_new_item.syntax().clone_subtree()); + func_editor.replace(func_body.syntax(), body.syntax()); + ast::AssocItem::cast(func_editor.finish().new_root().clone()) + } else { + Some(first_new_item.clone()) + }; + + let new_assoc_items = first_new_item + .clone() + .into_iter() + .chain(other_items.iter().cloned()) + .map(either::Either::Right) + .collect::>(); + + let mut editor = edit.make_editor(impl_def.syntax()); + if let Some(assoc_item_list) = impl_def.assoc_item_list() { + let items = new_assoc_items.into_iter().filter_map(either::Either::right).collect(); + assoc_item_list.add_items(&mut editor, items); + } else { + let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update(); + editor.insert_all( + Position::after(impl_def.syntax()), + vec![make::tokens::whitespace(" ").into(), assoc_item_list.syntax().clone().into()], + ); + first_new_item = assoc_item_list.assoc_items().next(); + } + if let Some(cap) = ctx.config.snippet_cap { let mut placeholder = None; if let DefaultMethods::No = mode { - if let ast::AssocItem::Fn(func) = &first_new_item { - if let Some(body) = try_gen_trait_body( - ctx, - func, - trait_ref, - &impl_def, - target_scope.krate().edition(ctx.sema.db), - ) && let Some(func_body) = func.body() - { - ted::replace(func_body.syntax(), body.syntax()); - } else if let Some(m) = - func.syntax().descendants().find_map(ast::MacroCall::cast) + if let Some(ast::AssocItem::Fn(func)) = &first_new_item { + if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) && m.syntax().text() == "todo!()" { placeholder = Some(m); @@ -181,11 +212,14 @@ fn add_missing_impl_members_inner( } if let Some(macro_call) = placeholder { - edit.add_placeholder_snippet(cap, macro_call); - } else { - edit.add_tabstop_before(cap, first_new_item); + let placeholder = edit.make_placeholder_snippet(cap); + editor.add_annotation(macro_call.syntax(), placeholder); + } else if let Some(first_new_item) = first_new_item { + let tabstop = edit.make_tabstop_before(cap); + editor.add_annotation(first_new_item.syntax(), tabstop); }; }; + edit.add_file_edits(ctx.vfs_file_id(), editor); }) } @@ -322,7 +356,7 @@ impl Foo for S { } #[test] - fn test_impl_def_without_braces() { + fn test_impl_def_without_braces_macro() { check_assist( add_missing_impl_members, r#" @@ -340,6 +374,33 @@ impl Foo for S { ); } + #[test] + fn test_impl_def_without_braces_tabstop_first_item() { + check_assist( + add_missing_impl_members, + r#" +trait Foo { + type Output; + fn foo(&self); +} +struct S; +impl Foo for S { $0 }"#, + r#" +trait Foo { + type Output; + fn foo(&self); +} +struct S; +impl Foo for S { + $0type Output; + + fn foo(&self) { + todo!() + } +}"#, + ); + } + #[test] fn fill_in_type_params_1() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 168eba22b3ba..31cadcf5ea86 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -167,25 +167,33 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> DefaultMethods::No, IgnoreAssocItems::DocHiddenAttrPresent, ); - let impl_ = make::impl_trait( - trait_.unsafe_token().is_some(), - None, - trait_.generic_param_list().map(|list| { - make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone())) - }), - None, - None, - false, - make::ty(&name.text()), - make::ty_placeholder(), - None, - None, - None, - ) - .clone_for_update(); - if !missing_items.is_empty() { - utils::add_trait_assoc_items_to_impl( + let trait_gen_args = trait_.generic_param_list().map(|list| { + make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone())) + }); + + let make_impl_ = |body| { + make::impl_trait( + trait_.unsafe_token().is_some(), + None, + trait_gen_args.clone(), + None, + None, + false, + make::ty(&name.text()), + make::ty_placeholder(), + None, + None, + body, + ) + .clone_for_update() + }; + + let impl_ = if missing_items.is_empty() { + make_impl_(None) + } else { + let impl_ = make_impl_(None); + let assoc_items = utils::add_trait_assoc_items_to_impl( &ctx.sema, ctx.config, &missing_items, @@ -193,7 +201,10 @@ pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> &impl_, &target_scope, ); - } + let assoc_items = assoc_items.into_iter().map(either::Either::Right).collect(); + let assoc_item_list = make::assoc_item_list(Some(assoc_items)); + make_impl_(Some(assoc_item_list)) + }; if let Some(cap) = ctx.config.snippet_cap { if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 2f979b7ee07b..45bb6ce9129c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -206,19 +206,35 @@ fn impl_def_from_trait( } let impl_def = generate_trait_impl(impl_is_unsafe, adt, make::ty_path(trait_path.clone())); - let _ = + let assoc_items = add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope); - let impl_def = impl_def.clone_subtree(); - let mut editor = SyntaxEditor::new(impl_def.syntax().clone()); - let first_assoc_item = impl_def.assoc_item_list().and_then(|item| item.assoc_items().next())?; - // Generate a default `impl` function body for the derived trait. - if let ast::AssocItem::Fn(ref func) = first_assoc_item { - if let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) + let assoc_item_list = if let Some((first, other)) = + assoc_items.split_first().map(|(first, other)| (first.clone_subtree(), other)) + { + let first_item = if let ast::AssocItem::Fn(ref func) = first + && let Some(body) = gen_trait_fn_body(func, trait_path, adt, None) && let Some(func_body) = func.body() { + let mut editor = SyntaxEditor::new(first.syntax().clone()); editor.replace(func_body.syntax(), body.syntax()); - } - }; + ast::AssocItem::cast(editor.finish().new_root().clone()) + } else { + Some(first.clone()) + }; + let items = first_item + .into_iter() + .chain(other.iter().cloned()) + .map(either::Either::Right) + .collect(); + make::assoc_item_list(Some(items)) + } else { + make::assoc_item_list(None) + } + .clone_for_update(); + + let impl_def = impl_def.clone_subtree(); + let mut editor = SyntaxEditor::new(impl_def.syntax().clone()); + editor.replace(impl_def.assoc_item_list()?.syntax(), assoc_item_list.syntax()); let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?; Some(impl_def) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index bc3fdf10d7a2..3457367645f0 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -23,7 +23,7 @@ use syntax::{ ast::{ self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, edit::{AstNodeEdit, IndentLevel}, - edit_in_place::{AttrsOwnerEdit, Indent, Removable}, + edit_in_place::{AttrsOwnerEdit, Removable}, make, syntax_factory::SyntaxFactory, }, @@ -178,6 +178,7 @@ pub fn filter_assoc_items( /// [`filter_assoc_items()`]), clones each item for update and applies path transformation to it, /// then inserts into `impl_`. Returns the modified `impl_` and the first associated item that got /// inserted. +#[must_use] pub fn add_trait_assoc_items_to_impl( sema: &Semantics<'_, RootDatabase>, config: &AssistConfig, @@ -185,71 +186,65 @@ pub fn add_trait_assoc_items_to_impl( trait_: hir::Trait, impl_: &ast::Impl, target_scope: &hir::SemanticsScope<'_>, -) -> ast::AssocItem { +) -> Vec { let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; - let items = original_items.iter().map(|InFile { file_id, value: original_item }| { - let cloned_item = { - if let Some(macro_file) = file_id.macro_file() { - let span_map = sema.db.expansion_span_map(macro_file); - let item_prettified = prettify_macro_expansion( - sema.db, - original_item.syntax().clone(), - &span_map, - target_scope.krate().into(), - ); - if let Some(formatted) = ast::AssocItem::cast(item_prettified) { - return formatted; - } else { - stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); + original_items + .iter() + .map(|InFile { file_id, value: original_item }| { + let cloned_item = { + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let item_prettified = prettify_macro_expansion( + sema.db, + original_item.syntax().clone(), + &span_map, + target_scope.krate().into(), + ); + if let Some(formatted) = ast::AssocItem::cast(item_prettified) { + return formatted; + } else { + stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); + } } + original_item.clone_for_update() + }; + + if let Some(source_scope) = sema.scope(original_item.syntax()) { + // FIXME: Paths in nested macros are not handled well. See + // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test. + let transform = + PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); + transform.apply(cloned_item.syntax()); } - original_item.clone_for_update() - }; - - if let Some(source_scope) = sema.scope(original_item.syntax()) { - // FIXME: Paths in nested macros are not handled well. See - // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test. - let transform = - PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); - transform.apply(cloned_item.syntax()); - } - cloned_item.remove_attrs_and_docs(); - cloned_item.reindent_to(new_indent_level); - cloned_item - }); - - let assoc_item_list = impl_.get_or_create_assoc_item_list(); - - let mut first_item = None; - for item in items { - first_item.get_or_insert_with(|| item.clone()); - match &item { - ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { - let body = AstNodeEdit::indent( - &make::block_expr( - None, - Some(match config.expr_fill_default { - ExprFillDefaultMode::Todo => make::ext::expr_todo(), - ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), - ExprFillDefaultMode::Default => make::ext::expr_todo(), - }), - ), - new_indent_level, - ); - ted::replace(fn_.get_or_create_body().syntax(), body.syntax()) - } - ast::AssocItem::TypeAlias(type_alias) => { - if let Some(type_bound_list) = type_alias.type_bound_list() { - type_bound_list.remove() + cloned_item.remove_attrs_and_docs(); + cloned_item.reset_indent() + }) + .map(|item| { + match &item { + ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { + let body = AstNodeEdit::indent( + &make::block_expr( + None, + Some(match config.expr_fill_default { + ExprFillDefaultMode::Todo => make::ext::expr_todo(), + ExprFillDefaultMode::Underscore => make::ext::expr_underscore(), + ExprFillDefaultMode::Default => make::ext::expr_todo(), + }), + ), + IndentLevel::single(), + ); + ted::replace(fn_.get_or_create_body().syntax(), body.syntax()); } + ast::AssocItem::TypeAlias(type_alias) => { + if let Some(type_bound_list) = type_alias.type_bound_list() { + type_bound_list.remove() + } + } + _ => {} } - _ => {} - } - - assoc_item_list.add_item(item) - } - - first_item.unwrap() + AstNodeEdit::indent(&item, new_indent_level) + }) + .collect() } pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index 66aae101eb00..2a7b51c3c248 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -237,7 +237,7 @@ pub fn assoc_item_list( let body_indent = if is_break_braces { " ".to_owned() } else { String::new() }; let body = match body { - Some(bd) => bd.iter().map(|elem| elem.to_string()).join(""), + Some(bd) => bd.iter().map(|elem| elem.to_string()).join("\n\n "), None => String::new(), }; ast_from_text(&format!("impl C for D {{{body_newline}{body_indent}{body}{body_newline}}}")) diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs index d66ea8aa28cf..840e76979792 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edits.rs @@ -92,6 +92,42 @@ fn get_or_insert_comma_after(editor: &mut SyntaxEditor, syntax: &SyntaxNode) -> } } +impl ast::AssocItemList { + /// Adds a new associated item after all of the existing associated items. + /// + /// Attention! This function does align the first line of `item` with respect to `self`, + /// but it does _not_ change indentation of other lines (if any). + pub fn add_items(&self, editor: &mut SyntaxEditor, items: Vec) { + let (indent, position, whitespace) = match self.assoc_items().last() { + Some(last_item) => ( + IndentLevel::from_node(last_item.syntax()), + Position::after(last_item.syntax()), + "\n\n", + ), + None => match self.l_curly_token() { + Some(l_curly) => { + normalize_ws_between_braces(editor, self.syntax()); + (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n") + } + None => (IndentLevel::single(), Position::last_child_of(self.syntax()), "\n"), + }, + }; + + let elements: Vec = items + .into_iter() + .enumerate() + .flat_map(|(i, item)| { + let whitespace = if i != 0 { "\n\n" } else { whitespace }; + vec![ + make::tokens::whitespace(&format!("{whitespace}{indent}")).into(), + item.syntax().clone().into(), + ] + }) + .collect(); + editor.insert_all(position, elements); + } +} + impl ast::VariantList { pub fn add_variant(&self, editor: &mut SyntaxEditor, variant: &ast::Variant) { let make = SyntaxFactory::without_mappings(); From 9f38ca97eab53ba2f431a48bec2343ef52335714 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 18 Jul 2025 22:00:30 +0500 Subject: [PATCH 058/201] move 28 tests --- .../issue-10396.rs => array-slice-vec/array-pattern-matching.rs} | 0 .../issue-11205.rs => array-slice-vec/trait-object-arrays.rs} | 0 .../issue-11192.rs => borrowck/closure-borrow-conflict.rs} | 0 .../issue-11085.rs => cfg/conditional-compilation-struct.rs} | 0 .../ui/{issues/issue-10718.rs => closures/fnonce-closure-call.rs} | 0 .../{issues/issue-10734.rs => drop/conditional-drop-behavior.rs} | 0 .../{issues/issue-10802.rs => drop/trait-object-drop-behavior.rs} | 0 .../issue-10764.rs => extern/extern-rust-fn-type-error.rs} | 0 .../{issues/issue-10877.rs => extern/foreign-fn-pattern-error.rs} | 0 tests/ui/{issues/issue-10767.rs => fn/boxed-fn-pointer.rs} | 0 .../{issues/issue-10436.rs => generics/generic-type-inference.rs} | 0 .../ui/{issues/issue-10806.rs => imports/empty-use-statements.rs} | 0 .../issue-10291.rs => lifetimes/closure-lifetime-bounds-error.rs} | 0 .../issue-11374.rs => lifetimes/container-lifetime-error.rs} | 0 .../issue-10228.rs => lifetimes/enum-lifetime-container.rs} | 0 .../issue-10412.rs => lifetimes/keyword-self-lifetime-error.rs} | 0 .../{issues/issue-10902.rs => lifetimes/trait-lifetime-bounds.rs} | 0 tests/ui/{issues/issue-10853.rs => lint/inner-doc-attributes.rs} | 0 tests/ui/{issues/issue-10638.rs => parser/comment-parsing.rs} | 0 .../{issues/issue-10683.rs => pattern/ascii-lowercase-match.rs} | 0 .../issue-10545.rs => privacy/private-struct-access-error.rs} | 0 .../issue-11267.rs => structs/mutable-unit-struct-borrow.rs} | 0 .../issue-10456.rs => traits/blanket-impl-trait-object.rs} | 0 .../issue-10465.rs => traits/missing-trait-method-error.rs} | 0 .../issue-106755.rs => traits/negative-positive-impl-conflict.rs} | 0 .../issue-102964.rs => type-alias/mismatched-rc-foo-types.rs} | 0 .../issue-11047.rs => type-alias/static-method-type-alias.rs} | 0 .../issue-11004.rs => unsafe/raw-pointer-field-access-error.rs} | 0 28 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-10396.rs => array-slice-vec/array-pattern-matching.rs} (100%) rename tests/ui/{issues/issue-11205.rs => array-slice-vec/trait-object-arrays.rs} (100%) rename tests/ui/{issues/issue-11192.rs => borrowck/closure-borrow-conflict.rs} (100%) rename tests/ui/{issues/issue-11085.rs => cfg/conditional-compilation-struct.rs} (100%) rename tests/ui/{issues/issue-10718.rs => closures/fnonce-closure-call.rs} (100%) rename tests/ui/{issues/issue-10734.rs => drop/conditional-drop-behavior.rs} (100%) rename tests/ui/{issues/issue-10802.rs => drop/trait-object-drop-behavior.rs} (100%) rename tests/ui/{issues/issue-10764.rs => extern/extern-rust-fn-type-error.rs} (100%) rename tests/ui/{issues/issue-10877.rs => extern/foreign-fn-pattern-error.rs} (100%) rename tests/ui/{issues/issue-10767.rs => fn/boxed-fn-pointer.rs} (100%) rename tests/ui/{issues/issue-10436.rs => generics/generic-type-inference.rs} (100%) rename tests/ui/{issues/issue-10806.rs => imports/empty-use-statements.rs} (100%) rename tests/ui/{issues/issue-10291.rs => lifetimes/closure-lifetime-bounds-error.rs} (100%) rename tests/ui/{issues/issue-11374.rs => lifetimes/container-lifetime-error.rs} (100%) rename tests/ui/{issues/issue-10228.rs => lifetimes/enum-lifetime-container.rs} (100%) rename tests/ui/{issues/issue-10412.rs => lifetimes/keyword-self-lifetime-error.rs} (100%) rename tests/ui/{issues/issue-10902.rs => lifetimes/trait-lifetime-bounds.rs} (100%) rename tests/ui/{issues/issue-10853.rs => lint/inner-doc-attributes.rs} (100%) rename tests/ui/{issues/issue-10638.rs => parser/comment-parsing.rs} (100%) rename tests/ui/{issues/issue-10683.rs => pattern/ascii-lowercase-match.rs} (100%) rename tests/ui/{issues/issue-10545.rs => privacy/private-struct-access-error.rs} (100%) rename tests/ui/{issues/issue-11267.rs => structs/mutable-unit-struct-borrow.rs} (100%) rename tests/ui/{issues/issue-10456.rs => traits/blanket-impl-trait-object.rs} (100%) rename tests/ui/{issues/issue-10465.rs => traits/missing-trait-method-error.rs} (100%) rename tests/ui/{issues/issue-106755.rs => traits/negative-positive-impl-conflict.rs} (100%) rename tests/ui/{issues/issue-102964.rs => type-alias/mismatched-rc-foo-types.rs} (100%) rename tests/ui/{issues/issue-11047.rs => type-alias/static-method-type-alias.rs} (100%) rename tests/ui/{issues/issue-11004.rs => unsafe/raw-pointer-field-access-error.rs} (100%) diff --git a/tests/ui/issues/issue-10396.rs b/tests/ui/array-slice-vec/array-pattern-matching.rs similarity index 100% rename from tests/ui/issues/issue-10396.rs rename to tests/ui/array-slice-vec/array-pattern-matching.rs diff --git a/tests/ui/issues/issue-11205.rs b/tests/ui/array-slice-vec/trait-object-arrays.rs similarity index 100% rename from tests/ui/issues/issue-11205.rs rename to tests/ui/array-slice-vec/trait-object-arrays.rs diff --git a/tests/ui/issues/issue-11192.rs b/tests/ui/borrowck/closure-borrow-conflict.rs similarity index 100% rename from tests/ui/issues/issue-11192.rs rename to tests/ui/borrowck/closure-borrow-conflict.rs diff --git a/tests/ui/issues/issue-11085.rs b/tests/ui/cfg/conditional-compilation-struct.rs similarity index 100% rename from tests/ui/issues/issue-11085.rs rename to tests/ui/cfg/conditional-compilation-struct.rs diff --git a/tests/ui/issues/issue-10718.rs b/tests/ui/closures/fnonce-closure-call.rs similarity index 100% rename from tests/ui/issues/issue-10718.rs rename to tests/ui/closures/fnonce-closure-call.rs diff --git a/tests/ui/issues/issue-10734.rs b/tests/ui/drop/conditional-drop-behavior.rs similarity index 100% rename from tests/ui/issues/issue-10734.rs rename to tests/ui/drop/conditional-drop-behavior.rs diff --git a/tests/ui/issues/issue-10802.rs b/tests/ui/drop/trait-object-drop-behavior.rs similarity index 100% rename from tests/ui/issues/issue-10802.rs rename to tests/ui/drop/trait-object-drop-behavior.rs diff --git a/tests/ui/issues/issue-10764.rs b/tests/ui/extern/extern-rust-fn-type-error.rs similarity index 100% rename from tests/ui/issues/issue-10764.rs rename to tests/ui/extern/extern-rust-fn-type-error.rs diff --git a/tests/ui/issues/issue-10877.rs b/tests/ui/extern/foreign-fn-pattern-error.rs similarity index 100% rename from tests/ui/issues/issue-10877.rs rename to tests/ui/extern/foreign-fn-pattern-error.rs diff --git a/tests/ui/issues/issue-10767.rs b/tests/ui/fn/boxed-fn-pointer.rs similarity index 100% rename from tests/ui/issues/issue-10767.rs rename to tests/ui/fn/boxed-fn-pointer.rs diff --git a/tests/ui/issues/issue-10436.rs b/tests/ui/generics/generic-type-inference.rs similarity index 100% rename from tests/ui/issues/issue-10436.rs rename to tests/ui/generics/generic-type-inference.rs diff --git a/tests/ui/issues/issue-10806.rs b/tests/ui/imports/empty-use-statements.rs similarity index 100% rename from tests/ui/issues/issue-10806.rs rename to tests/ui/imports/empty-use-statements.rs diff --git a/tests/ui/issues/issue-10291.rs b/tests/ui/lifetimes/closure-lifetime-bounds-error.rs similarity index 100% rename from tests/ui/issues/issue-10291.rs rename to tests/ui/lifetimes/closure-lifetime-bounds-error.rs diff --git a/tests/ui/issues/issue-11374.rs b/tests/ui/lifetimes/container-lifetime-error.rs similarity index 100% rename from tests/ui/issues/issue-11374.rs rename to tests/ui/lifetimes/container-lifetime-error.rs diff --git a/tests/ui/issues/issue-10228.rs b/tests/ui/lifetimes/enum-lifetime-container.rs similarity index 100% rename from tests/ui/issues/issue-10228.rs rename to tests/ui/lifetimes/enum-lifetime-container.rs diff --git a/tests/ui/issues/issue-10412.rs b/tests/ui/lifetimes/keyword-self-lifetime-error.rs similarity index 100% rename from tests/ui/issues/issue-10412.rs rename to tests/ui/lifetimes/keyword-self-lifetime-error.rs diff --git a/tests/ui/issues/issue-10902.rs b/tests/ui/lifetimes/trait-lifetime-bounds.rs similarity index 100% rename from tests/ui/issues/issue-10902.rs rename to tests/ui/lifetimes/trait-lifetime-bounds.rs diff --git a/tests/ui/issues/issue-10853.rs b/tests/ui/lint/inner-doc-attributes.rs similarity index 100% rename from tests/ui/issues/issue-10853.rs rename to tests/ui/lint/inner-doc-attributes.rs diff --git a/tests/ui/issues/issue-10638.rs b/tests/ui/parser/comment-parsing.rs similarity index 100% rename from tests/ui/issues/issue-10638.rs rename to tests/ui/parser/comment-parsing.rs diff --git a/tests/ui/issues/issue-10683.rs b/tests/ui/pattern/ascii-lowercase-match.rs similarity index 100% rename from tests/ui/issues/issue-10683.rs rename to tests/ui/pattern/ascii-lowercase-match.rs diff --git a/tests/ui/issues/issue-10545.rs b/tests/ui/privacy/private-struct-access-error.rs similarity index 100% rename from tests/ui/issues/issue-10545.rs rename to tests/ui/privacy/private-struct-access-error.rs diff --git a/tests/ui/issues/issue-11267.rs b/tests/ui/structs/mutable-unit-struct-borrow.rs similarity index 100% rename from tests/ui/issues/issue-11267.rs rename to tests/ui/structs/mutable-unit-struct-borrow.rs diff --git a/tests/ui/issues/issue-10456.rs b/tests/ui/traits/blanket-impl-trait-object.rs similarity index 100% rename from tests/ui/issues/issue-10456.rs rename to tests/ui/traits/blanket-impl-trait-object.rs diff --git a/tests/ui/issues/issue-10465.rs b/tests/ui/traits/missing-trait-method-error.rs similarity index 100% rename from tests/ui/issues/issue-10465.rs rename to tests/ui/traits/missing-trait-method-error.rs diff --git a/tests/ui/issues/issue-106755.rs b/tests/ui/traits/negative-positive-impl-conflict.rs similarity index 100% rename from tests/ui/issues/issue-106755.rs rename to tests/ui/traits/negative-positive-impl-conflict.rs diff --git a/tests/ui/issues/issue-102964.rs b/tests/ui/type-alias/mismatched-rc-foo-types.rs similarity index 100% rename from tests/ui/issues/issue-102964.rs rename to tests/ui/type-alias/mismatched-rc-foo-types.rs diff --git a/tests/ui/issues/issue-11047.rs b/tests/ui/type-alias/static-method-type-alias.rs similarity index 100% rename from tests/ui/issues/issue-11047.rs rename to tests/ui/type-alias/static-method-type-alias.rs diff --git a/tests/ui/issues/issue-11004.rs b/tests/ui/unsafe/raw-pointer-field-access-error.rs similarity index 100% rename from tests/ui/issues/issue-11004.rs rename to tests/ui/unsafe/raw-pointer-field-access-error.rs From e9959aa74e23eb340d6c9e9a4eab807be03b028f Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Fri, 18 Jul 2025 22:06:07 +0500 Subject: [PATCH 059/201] comments --- tests/ui/README.md | 4 ++++ ...flict.rs => closure-borrow-conflict-11192.rs} | 2 ++ .../closure-borrow-conflict-11192.stderr} | 2 +- ...s => conditional-compilation-struct-11085.rs} | 2 ++ .../trait-object-arrays-11205.rs} | 2 ++ .../primary-fluent-bundle-missing.rs} | 2 ++ .../primary-fluent-bundle-missing.stderr} | 12 ++++++------ ...rop-behavior.rs => conditional-drop-10734.rs} | 2 ++ ...op-behavior.rs => trait-object-drop-10802.rs} | 2 ++ ...ror.rs => extern-rust-fn-type-error-10764.rs} | 2 ++ .../extern-rust-fn-type-error-10764.stderr} | 4 ++-- ...rror.rs => foreign-fn-pattern-error-10877.rs} | 2 ++ .../foreign-fn-pattern-error-10877.stderr} | 8 ++++---- tests/ui/fn/boxed-fn-pointer.rs | 7 ------- ...=> use-declaration-no-path-segment-prefix.rs} | 2 ++ .../fnonce-closure-call.rs | 2 ++ .../generic-type-inference-10436.rs} | 2 ++ .../array-pattern-matching-10396.rs} | 2 ++ ...error.rs => closure-lifetime-bounds-10291.rs} | 2 ++ .../closure-lifetime-bounds-10291.stderr} | 2 +- ...rror.rs => container-lifetime-error-11374.rs} | 2 ++ .../container-lifetime-error-11374.stderr} | 6 +++--- ...ainer.rs => enum-lifetime-container-10228.rs} | 2 ++ ...r.rs => keyword-self-lifetime-error-10412.rs} | 2 ++ .../keyword-self-lifetime-error-10412.stderr} | 16 ++++++++-------- ...ruct-vs-struct-with-fields-borrowck-10902.rs} | 2 ++ ...rs => missing-doc-unsugard-doc-attr-10853.rs} | 2 ++ ...comment-parsing.rs => doc-comment-parsing.rs} | 2 ++ ...ture-match-scrutinee-temporary-drop-10683.rs} | 2 ++ ....rs => struct-field-and-impl-expose-10545.rs} | 2 ++ .../struct-field-and-impl-expose-10545.stderr} | 4 ++-- ...ow.rs => mutable-unit-struct-borrow-11267.rs} | 2 ++ ...ect.rs => blanket-impl-trait-object-10456.rs} | 2 ++ ...nested-mod-trait-method-lookup-leak-10465.rs} | 2 ++ ...ed-mod-trait-method-lookup-leak-10465.stderr} | 2 +- ...ed-rc-foo-types.rs => dummy-binder-102964.rs} | 2 ++ .../dummy-binder-102964.stderr} | 2 +- ...lias.rs => static-method-type-alias-11047.rs} | 2 ++ .../ui/unsafe/raw-pointer-field-access-error.rs | 2 ++ .../raw-pointer-field-access-error.stderr} | 4 ++-- 40 files changed, 89 insertions(+), 38 deletions(-) rename tests/ui/borrowck/{closure-borrow-conflict.rs => closure-borrow-conflict-11192.rs} (85%) rename tests/ui/{issues/issue-11192.stderr => borrowck/closure-borrow-conflict-11192.stderr} (92%) rename tests/ui/cfg/{conditional-compilation-struct.rs => conditional-compilation-struct-11085.rs} (88%) rename tests/ui/{array-slice-vec/trait-object-arrays.rs => coercion/trait-object-arrays-11205.rs} (95%) rename tests/ui/{traits/negative-positive-impl-conflict.rs => diagnostics-infra/primary-fluent-bundle-missing.rs} (89%) rename tests/ui/{issues/issue-106755.stderr => diagnostics-infra/primary-fluent-bundle-missing.stderr} (84%) rename tests/ui/drop/{conditional-drop-behavior.rs => conditional-drop-10734.rs} (93%) rename tests/ui/drop/{trait-object-drop-behavior.rs => trait-object-drop-10802.rs} (93%) rename tests/ui/extern/{extern-rust-fn-type-error.rs => extern-rust-fn-type-error-10764.rs} (59%) rename tests/ui/{issues/issue-10764.stderr => extern/extern-rust-fn-type-error-10764.stderr} (83%) rename tests/ui/extern/{foreign-fn-pattern-error.rs => foreign-fn-pattern-error-10877.rs} (87%) rename tests/ui/{issues/issue-10877.stderr => extern/foreign-fn-pattern-error-10877.stderr} (79%) delete mode 100644 tests/ui/fn/boxed-fn-pointer.rs rename tests/ui/imports/{empty-use-statements.rs => use-declaration-no-path-segment-prefix.rs} (87%) rename tests/ui/{closures => inference}/fnonce-closure-call.rs (57%) rename tests/ui/{generics/generic-type-inference.rs => inference/generic-type-inference-10436.rs} (77%) rename tests/ui/{array-slice-vec/array-pattern-matching.rs => lifetimes/array-pattern-matching-10396.rs} (71%) rename tests/ui/lifetimes/{closure-lifetime-bounds-error.rs => closure-lifetime-bounds-10291.rs} (72%) rename tests/ui/{issues/issue-10291.stderr => lifetimes/closure-lifetime-bounds-10291.stderr} (87%) rename tests/ui/lifetimes/{container-lifetime-error.rs => container-lifetime-error-11374.rs} (89%) rename tests/ui/{issues/issue-11374.stderr => lifetimes/container-lifetime-error-11374.stderr} (86%) rename tests/ui/lifetimes/{enum-lifetime-container.rs => enum-lifetime-container-10228.rs} (80%) rename tests/ui/lifetimes/{keyword-self-lifetime-error.rs => keyword-self-lifetime-error-10412.rs} (91%) rename tests/ui/{issues/issue-10412.stderr => lifetimes/keyword-self-lifetime-error-10412.stderr} (76%) rename tests/ui/lifetimes/{trait-lifetime-bounds.rs => tuple-struct-vs-struct-with-fields-borrowck-10902.rs} (87%) rename tests/ui/lint/{inner-doc-attributes.rs => missing-doc-unsugard-doc-attr-10853.rs} (68%) rename tests/ui/parser/{comment-parsing.rs => doc-comment-parsing.rs} (74%) rename tests/ui/pattern/{ascii-lowercase-match.rs => premature-match-scrutinee-temporary-drop-10683.rs} (68%) rename tests/ui/privacy/{private-struct-access-error.rs => struct-field-and-impl-expose-10545.rs} (59%) rename tests/ui/{issues/issue-10545.stderr => privacy/struct-field-and-impl-expose-10545.stderr} (73%) rename tests/ui/structs/{mutable-unit-struct-borrow.rs => mutable-unit-struct-borrow-11267.rs} (82%) rename tests/ui/traits/{blanket-impl-trait-object.rs => blanket-impl-trait-object-10456.rs} (81%) rename tests/ui/traits/{missing-trait-method-error.rs => nested-mod-trait-method-lookup-leak-10465.rs} (80%) rename tests/ui/{issues/issue-10465.stderr => traits/nested-mod-trait-method-lookup-leak-10465.stderr} (88%) rename tests/ui/type-alias/{mismatched-rc-foo-types.rs => dummy-binder-102964.rs} (75%) rename tests/ui/{issues/issue-102964.stderr => type-alias/dummy-binder-102964.stderr} (93%) rename tests/ui/type-alias/{static-method-type-alias.rs => static-method-type-alias-11047.rs} (87%) rename tests/ui/{issues/issue-11004.stderr => unsafe/raw-pointer-field-access-error.stderr} (85%) diff --git a/tests/ui/README.md b/tests/ui/README.md index b635b6326fce..3630ce9285da 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -412,6 +412,10 @@ These tests revolve around command-line flags which change the way error/warning Exercises `#[diagnostic::*]` namespaced attributes. See [RFC 3368 Diagnostic attribute namespace](https://github.com/rust-lang/rfcs/blob/master/text/3368-diagnostic-attribute-namespace.md). +## `tests/ui/diagnostics-infra` + +This directory contains tests and infrastructure related to the diagnostics system, including support for translatable diagnostics + ## `tests/ui/diagnostic-width/`: `--diagnostic-width` Everything to do with `--diagnostic-width`. diff --git a/tests/ui/borrowck/closure-borrow-conflict.rs b/tests/ui/borrowck/closure-borrow-conflict-11192.rs similarity index 85% rename from tests/ui/borrowck/closure-borrow-conflict.rs rename to tests/ui/borrowck/closure-borrow-conflict-11192.rs index 1a3d8c9fe586..dff70d62d6f4 100644 --- a/tests/ui/borrowck/closure-borrow-conflict.rs +++ b/tests/ui/borrowck/closure-borrow-conflict-11192.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11192 + struct Foo { x: isize } diff --git a/tests/ui/issues/issue-11192.stderr b/tests/ui/borrowck/closure-borrow-conflict-11192.stderr similarity index 92% rename from tests/ui/issues/issue-11192.stderr rename to tests/ui/borrowck/closure-borrow-conflict-11192.stderr index a8a18c49549d..f1df635276b4 100644 --- a/tests/ui/issues/issue-11192.stderr +++ b/tests/ui/borrowck/closure-borrow-conflict-11192.stderr @@ -1,5 +1,5 @@ error[E0502]: cannot borrow `*ptr` as immutable because it is also borrowed as mutable - --> $DIR/issue-11192.rs:20:10 + --> $DIR/closure-borrow-conflict-11192.rs:22:10 | LL | let mut test = |foo: &Foo| { | ----------- mutable borrow occurs here diff --git a/tests/ui/cfg/conditional-compilation-struct.rs b/tests/ui/cfg/conditional-compilation-struct-11085.rs similarity index 88% rename from tests/ui/cfg/conditional-compilation-struct.rs rename to tests/ui/cfg/conditional-compilation-struct-11085.rs index c3f13199b308..cd6dded54d30 100644 --- a/tests/ui/cfg/conditional-compilation-struct.rs +++ b/tests/ui/cfg/conditional-compilation-struct-11085.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11085 + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/array-slice-vec/trait-object-arrays.rs b/tests/ui/coercion/trait-object-arrays-11205.rs similarity index 95% rename from tests/ui/array-slice-vec/trait-object-arrays.rs rename to tests/ui/coercion/trait-object-arrays-11205.rs index 8530514f0edf..45d69dce3238 100644 --- a/tests/ui/array-slice-vec/trait-object-arrays.rs +++ b/tests/ui/coercion/trait-object-arrays-11205.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11205 + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/traits/negative-positive-impl-conflict.rs b/tests/ui/diagnostics-infra/primary-fluent-bundle-missing.rs similarity index 89% rename from tests/ui/traits/negative-positive-impl-conflict.rs rename to tests/ui/diagnostics-infra/primary-fluent-bundle-missing.rs index d7e7122ebda1..f2965778431c 100644 --- a/tests/ui/traits/negative-positive-impl-conflict.rs +++ b/tests/ui/diagnostics-infra/primary-fluent-bundle-missing.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/106755 + //@ compile-flags:-Ztranslate-lang=en_US #![feature(negative_impls)] diff --git a/tests/ui/issues/issue-106755.stderr b/tests/ui/diagnostics-infra/primary-fluent-bundle-missing.stderr similarity index 84% rename from tests/ui/issues/issue-106755.stderr rename to tests/ui/diagnostics-infra/primary-fluent-bundle-missing.stderr index da6b8c5c5632..1dc31e161a76 100644 --- a/tests/ui/issues/issue-106755.stderr +++ b/tests/ui/diagnostics-infra/primary-fluent-bundle-missing.stderr @@ -1,5 +1,5 @@ error[E0751]: found both positive and negative implementation of trait `Send` for type `TestType<_>`: - --> $DIR/issue-106755.rs:13:1 + --> $DIR/primary-fluent-bundle-missing.rs:15:1 | LL | unsafe impl Send for TestType {} | ------------------------------------------------------ positive implementation here @@ -8,7 +8,7 @@ LL | impl !Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ negative implementation here error[E0119]: conflicting implementations of trait `Send` for type `TestType<_>` - --> $DIR/issue-106755.rs:17:1 + --> $DIR/primary-fluent-bundle-missing.rs:19:1 | LL | unsafe impl Send for TestType {} | ------------------------------------------------------ first implementation here @@ -17,26 +17,26 @@ LL | unsafe impl Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `TestType<_>` error[E0367]: `!Send` impl requires `T: MyTrait` but the struct it is implemented for does not - --> $DIR/issue-106755.rs:13:9 + --> $DIR/primary-fluent-bundle-missing.rs:15:9 | LL | impl !Send for TestType {} | ^^^^^^^ | note: the implementor must specify the same requirement - --> $DIR/issue-106755.rs:9:1 + --> $DIR/primary-fluent-bundle-missing.rs:11:1 | LL | struct TestType(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^ error[E0366]: `!Send` impls cannot be specialized - --> $DIR/issue-106755.rs:19:1 + --> $DIR/primary-fluent-bundle-missing.rs:21:1 | LL | impl !Send for TestType {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `i32` is not a generic parameter note: use the same sequence of generic lifetime, type and const parameters as the struct definition - --> $DIR/issue-106755.rs:9:1 + --> $DIR/primary-fluent-bundle-missing.rs:11:1 | LL | struct TestType(::std::marker::PhantomData); | ^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/drop/conditional-drop-behavior.rs b/tests/ui/drop/conditional-drop-10734.rs similarity index 93% rename from tests/ui/drop/conditional-drop-behavior.rs rename to tests/ui/drop/conditional-drop-10734.rs index 6d815aeca076..25f492bf9e08 100644 --- a/tests/ui/drop/conditional-drop-behavior.rs +++ b/tests/ui/drop/conditional-drop-10734.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10734 + //@ run-pass #![allow(non_upper_case_globals)] diff --git a/tests/ui/drop/trait-object-drop-behavior.rs b/tests/ui/drop/trait-object-drop-10802.rs similarity index 93% rename from tests/ui/drop/trait-object-drop-behavior.rs rename to tests/ui/drop/trait-object-drop-10802.rs index eca701ce98c9..a8a955ad8334 100644 --- a/tests/ui/drop/trait-object-drop-behavior.rs +++ b/tests/ui/drop/trait-object-drop-10802.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10802 + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/extern/extern-rust-fn-type-error.rs b/tests/ui/extern/extern-rust-fn-type-error-10764.rs similarity index 59% rename from tests/ui/extern/extern-rust-fn-type-error.rs rename to tests/ui/extern/extern-rust-fn-type-error-10764.rs index bb915f58d9d2..f172f6e6b7d9 100644 --- a/tests/ui/extern/extern-rust-fn-type-error.rs +++ b/tests/ui/extern/extern-rust-fn-type-error-10764.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10764 + fn f(_: extern "Rust" fn()) {} extern "C" fn bar() {} diff --git a/tests/ui/issues/issue-10764.stderr b/tests/ui/extern/extern-rust-fn-type-error-10764.stderr similarity index 83% rename from tests/ui/issues/issue-10764.stderr rename to tests/ui/extern/extern-rust-fn-type-error-10764.stderr index f3bd0100a72a..fa72d7dd6b2f 100644 --- a/tests/ui/issues/issue-10764.stderr +++ b/tests/ui/extern/extern-rust-fn-type-error-10764.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-10764.rs:4:15 + --> $DIR/extern-rust-fn-type-error-10764.rs:6:15 | LL | fn main() { f(bar) } | - ^^^ expected "Rust" fn, found "C" fn @@ -9,7 +9,7 @@ LL | fn main() { f(bar) } = note: expected fn pointer `fn()` found fn item `extern "C" fn() {bar}` note: function defined here - --> $DIR/issue-10764.rs:1:4 + --> $DIR/extern-rust-fn-type-error-10764.rs:3:4 | LL | fn f(_: extern "Rust" fn()) {} | ^ --------------------- diff --git a/tests/ui/extern/foreign-fn-pattern-error.rs b/tests/ui/extern/foreign-fn-pattern-error-10877.rs similarity index 87% rename from tests/ui/extern/foreign-fn-pattern-error.rs rename to tests/ui/extern/foreign-fn-pattern-error-10877.rs index 15a383175b97..9a047d4f34e7 100644 --- a/tests/ui/extern/foreign-fn-pattern-error.rs +++ b/tests/ui/extern/foreign-fn-pattern-error-10877.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10877 + struct Foo { x: isize, } diff --git a/tests/ui/issues/issue-10877.stderr b/tests/ui/extern/foreign-fn-pattern-error-10877.stderr similarity index 79% rename from tests/ui/issues/issue-10877.stderr rename to tests/ui/extern/foreign-fn-pattern-error-10877.stderr index bd3797cba558..cab7b6ab06be 100644 --- a/tests/ui/issues/issue-10877.stderr +++ b/tests/ui/extern/foreign-fn-pattern-error-10877.stderr @@ -1,23 +1,23 @@ error[E0130]: patterns aren't allowed in foreign function declarations - --> $DIR/issue-10877.rs:5:12 + --> $DIR/foreign-fn-pattern-error-10877.rs:7:12 | LL | fn foo(1: ()); | ^ pattern not allowed in foreign function error[E0130]: patterns aren't allowed in foreign function declarations - --> $DIR/issue-10877.rs:7:12 + --> $DIR/foreign-fn-pattern-error-10877.rs:9:12 | LL | fn bar((): isize); | ^^ pattern not allowed in foreign function error[E0130]: patterns aren't allowed in foreign function declarations - --> $DIR/issue-10877.rs:9:12 + --> $DIR/foreign-fn-pattern-error-10877.rs:11:12 | LL | fn baz(Foo { x }: isize); | ^^^^^^^^^ pattern not allowed in foreign function error[E0130]: patterns aren't allowed in foreign function declarations - --> $DIR/issue-10877.rs:11:12 + --> $DIR/foreign-fn-pattern-error-10877.rs:13:12 | LL | fn qux((x, y): ()); | ^^^^^^ pattern not allowed in foreign function diff --git a/tests/ui/fn/boxed-fn-pointer.rs b/tests/ui/fn/boxed-fn-pointer.rs deleted file mode 100644 index 2060d15b4c78..000000000000 --- a/tests/ui/fn/boxed-fn-pointer.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass - -pub fn main() { - fn f() { - } - let _: Box = Box::new(f as fn()); -} diff --git a/tests/ui/imports/empty-use-statements.rs b/tests/ui/imports/use-declaration-no-path-segment-prefix.rs similarity index 87% rename from tests/ui/imports/empty-use-statements.rs rename to tests/ui/imports/use-declaration-no-path-segment-prefix.rs index 31315dc7c93a..f7fbc084ebfe 100644 --- a/tests/ui/imports/empty-use-statements.rs +++ b/tests/ui/imports/use-declaration-no-path-segment-prefix.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10806 + //@ edition: 2015 //@ run-pass #![allow(unused_imports)] diff --git a/tests/ui/closures/fnonce-closure-call.rs b/tests/ui/inference/fnonce-closure-call.rs similarity index 57% rename from tests/ui/closures/fnonce-closure-call.rs rename to tests/ui/inference/fnonce-closure-call.rs index 68ac0bbe49fb..262a193609fc 100644 --- a/tests/ui/closures/fnonce-closure-call.rs +++ b/tests/ui/inference/fnonce-closure-call.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10718 + //@ run-pass fn f(p: F) { diff --git a/tests/ui/generics/generic-type-inference.rs b/tests/ui/inference/generic-type-inference-10436.rs similarity index 77% rename from tests/ui/generics/generic-type-inference.rs rename to tests/ui/inference/generic-type-inference-10436.rs index 672aa2464dc1..456a9b86c347 100644 --- a/tests/ui/generics/generic-type-inference.rs +++ b/tests/ui/inference/generic-type-inference-10436.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10436 + //@ run-pass fn works(x: T) -> Vec { vec![x] } diff --git a/tests/ui/array-slice-vec/array-pattern-matching.rs b/tests/ui/lifetimes/array-pattern-matching-10396.rs similarity index 71% rename from tests/ui/array-slice-vec/array-pattern-matching.rs rename to tests/ui/lifetimes/array-pattern-matching-10396.rs index 082216d557cf..5fc141bc4601 100644 --- a/tests/ui/array-slice-vec/array-pattern-matching.rs +++ b/tests/ui/lifetimes/array-pattern-matching-10396.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10396 + //@ check-pass #![allow(dead_code)] #[derive(Debug)] diff --git a/tests/ui/lifetimes/closure-lifetime-bounds-error.rs b/tests/ui/lifetimes/closure-lifetime-bounds-10291.rs similarity index 72% rename from tests/ui/lifetimes/closure-lifetime-bounds-error.rs rename to tests/ui/lifetimes/closure-lifetime-bounds-10291.rs index 31b9e1240461..42dc6c2cafad 100644 --- a/tests/ui/lifetimes/closure-lifetime-bounds-error.rs +++ b/tests/ui/lifetimes/closure-lifetime-bounds-10291.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10291 + fn test<'x>(x: &'x isize) { drop:: FnMut(&'z isize) -> &'z isize>>(Box::new(|z| { x diff --git a/tests/ui/issues/issue-10291.stderr b/tests/ui/lifetimes/closure-lifetime-bounds-10291.stderr similarity index 87% rename from tests/ui/issues/issue-10291.stderr rename to tests/ui/lifetimes/closure-lifetime-bounds-10291.stderr index 68ed9a0de5d5..34f8ca40871f 100644 --- a/tests/ui/issues/issue-10291.stderr +++ b/tests/ui/lifetimes/closure-lifetime-bounds-10291.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/issue-10291.rs:3:9 + --> $DIR/closure-lifetime-bounds-10291.rs:5:9 | LL | fn test<'x>(x: &'x isize) { | -- lifetime `'x` defined here diff --git a/tests/ui/lifetimes/container-lifetime-error.rs b/tests/ui/lifetimes/container-lifetime-error-11374.rs similarity index 89% rename from tests/ui/lifetimes/container-lifetime-error.rs rename to tests/ui/lifetimes/container-lifetime-error-11374.rs index 60ee256c65a9..59d13d04e466 100644 --- a/tests/ui/lifetimes/container-lifetime-error.rs +++ b/tests/ui/lifetimes/container-lifetime-error-11374.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11374 + use std::io::{self, Read}; use std::vec; diff --git a/tests/ui/issues/issue-11374.stderr b/tests/ui/lifetimes/container-lifetime-error-11374.stderr similarity index 86% rename from tests/ui/issues/issue-11374.stderr rename to tests/ui/lifetimes/container-lifetime-error-11374.stderr index 3ae5cfc79f87..a29b5ae137c2 100644 --- a/tests/ui/issues/issue-11374.stderr +++ b/tests/ui/lifetimes/container-lifetime-error-11374.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-11374.rs:27:15 + --> $DIR/container-lifetime-error-11374.rs:29:15 | LL | c.read_to(v); | ------- ^ expected `&mut [u8]`, found `Vec<_>` @@ -9,7 +9,7 @@ LL | c.read_to(v); = note: expected mutable reference `&mut [u8]` found struct `Vec<_>` note: method defined here - --> $DIR/issue-11374.rs:13:12 + --> $DIR/container-lifetime-error-11374.rs:15:12 | LL | pub fn read_to(&mut self, vec: &mut [u8]) { | ^^^^^^^ -------------- @@ -19,7 +19,7 @@ LL | c.read_to(&mut v); | ++++ error[E0515]: cannot return value referencing local variable `r` - --> $DIR/issue-11374.rs:20:5 + --> $DIR/container-lifetime-error-11374.rs:22:5 | LL | Container::wrap(&mut r as &mut dyn io::Read) | ^^^^^^^^^^^^^^^^------^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lifetimes/enum-lifetime-container.rs b/tests/ui/lifetimes/enum-lifetime-container-10228.rs similarity index 80% rename from tests/ui/lifetimes/enum-lifetime-container.rs rename to tests/ui/lifetimes/enum-lifetime-container-10228.rs index a59ccf926f9c..ebbefb619c61 100644 --- a/tests/ui/lifetimes/enum-lifetime-container.rs +++ b/tests/ui/lifetimes/enum-lifetime-container-10228.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10228 + //@ run-pass #![allow(dead_code)] #![allow(unused_variables)] diff --git a/tests/ui/lifetimes/keyword-self-lifetime-error.rs b/tests/ui/lifetimes/keyword-self-lifetime-error-10412.rs similarity index 91% rename from tests/ui/lifetimes/keyword-self-lifetime-error.rs rename to tests/ui/lifetimes/keyword-self-lifetime-error-10412.rs index 68ce0c2ea3cb..a5b303df2fd4 100644 --- a/tests/ui/lifetimes/keyword-self-lifetime-error.rs +++ b/tests/ui/lifetimes/keyword-self-lifetime-error-10412.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10412 + trait Serializable<'self, T> { //~^ ERROR lifetimes cannot use keyword names fn serialize(val: &'self T) -> Vec; //~ ERROR lifetimes cannot use keyword names diff --git a/tests/ui/issues/issue-10412.stderr b/tests/ui/lifetimes/keyword-self-lifetime-error-10412.stderr similarity index 76% rename from tests/ui/issues/issue-10412.stderr rename to tests/ui/lifetimes/keyword-self-lifetime-error-10412.stderr index c74ba1306cc4..236bdf1ac854 100644 --- a/tests/ui/issues/issue-10412.stderr +++ b/tests/ui/lifetimes/keyword-self-lifetime-error-10412.stderr @@ -1,47 +1,47 @@ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:1:20 + --> $DIR/keyword-self-lifetime-error-10412.rs:3:20 | LL | trait Serializable<'self, T> { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:3:24 + --> $DIR/keyword-self-lifetime-error-10412.rs:5:24 | LL | fn serialize(val: &'self T) -> Vec; | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:4:37 + --> $DIR/keyword-self-lifetime-error-10412.rs:6:37 | LL | fn deserialize(repr: &[u8]) -> &'self T; | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:7:6 + --> $DIR/keyword-self-lifetime-error-10412.rs:9:6 | LL | impl<'self> Serializable for &'self str { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:7:36 + --> $DIR/keyword-self-lifetime-error-10412.rs:9:36 | LL | impl<'self> Serializable for &'self str { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:11:24 + --> $DIR/keyword-self-lifetime-error-10412.rs:13:24 | LL | fn serialize(val: &'self str) -> Vec { | ^^^^^ error: lifetimes cannot use keyword names - --> $DIR/issue-10412.rs:15:37 + --> $DIR/keyword-self-lifetime-error-10412.rs:17:37 | LL | fn deserialize(repr: &[u8]) -> &'self str { | ^^^^^ error[E0726]: implicit elided lifetime not allowed here - --> $DIR/issue-10412.rs:7:13 + --> $DIR/keyword-self-lifetime-error-10412.rs:9:13 | LL | impl<'self> Serializable for &'self str { | ^^^^^^^^^^^^^^^^^ expected lifetime parameter diff --git a/tests/ui/lifetimes/trait-lifetime-bounds.rs b/tests/ui/lifetimes/tuple-struct-vs-struct-with-fields-borrowck-10902.rs similarity index 87% rename from tests/ui/lifetimes/trait-lifetime-bounds.rs rename to tests/ui/lifetimes/tuple-struct-vs-struct-with-fields-borrowck-10902.rs index 7cdf8808aa02..97c0d0bf554e 100644 --- a/tests/ui/lifetimes/trait-lifetime-bounds.rs +++ b/tests/ui/lifetimes/tuple-struct-vs-struct-with-fields-borrowck-10902.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10902 + //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/lint/inner-doc-attributes.rs b/tests/ui/lint/missing-doc-unsugard-doc-attr-10853.rs similarity index 68% rename from tests/ui/lint/inner-doc-attributes.rs rename to tests/ui/lint/missing-doc-unsugard-doc-attr-10853.rs index 4c22393d9c0a..ec13ae997878 100644 --- a/tests/ui/lint/inner-doc-attributes.rs +++ b/tests/ui/lint/missing-doc-unsugard-doc-attr-10853.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10853 + //@ check-pass #![deny(missing_docs)] diff --git a/tests/ui/parser/comment-parsing.rs b/tests/ui/parser/doc-comment-parsing.rs similarity index 74% rename from tests/ui/parser/comment-parsing.rs rename to tests/ui/parser/doc-comment-parsing.rs index c6c6939bda53..00f6b0e09a89 100644 --- a/tests/ui/parser/comment-parsing.rs +++ b/tests/ui/parser/doc-comment-parsing.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10638 + //@ run-pass pub fn main() { diff --git a/tests/ui/pattern/ascii-lowercase-match.rs b/tests/ui/pattern/premature-match-scrutinee-temporary-drop-10683.rs similarity index 68% rename from tests/ui/pattern/ascii-lowercase-match.rs rename to tests/ui/pattern/premature-match-scrutinee-temporary-drop-10683.rs index 5657ec1864b2..a4dfa56117c2 100644 --- a/tests/ui/pattern/ascii-lowercase-match.rs +++ b/tests/ui/pattern/premature-match-scrutinee-temporary-drop-10683.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10683 + //@ run-pass static NAME: &'static str = "hello world"; diff --git a/tests/ui/privacy/private-struct-access-error.rs b/tests/ui/privacy/struct-field-and-impl-expose-10545.rs similarity index 59% rename from tests/ui/privacy/private-struct-access-error.rs rename to tests/ui/privacy/struct-field-and-impl-expose-10545.rs index acd071496190..8a8c8240c2d0 100644 --- a/tests/ui/privacy/private-struct-access-error.rs +++ b/tests/ui/privacy/struct-field-and-impl-expose-10545.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10545 + mod a { struct S; impl S { } diff --git a/tests/ui/issues/issue-10545.stderr b/tests/ui/privacy/struct-field-and-impl-expose-10545.stderr similarity index 73% rename from tests/ui/issues/issue-10545.stderr rename to tests/ui/privacy/struct-field-and-impl-expose-10545.stderr index 9aa042171748..ddf87d1d23ad 100644 --- a/tests/ui/issues/issue-10545.stderr +++ b/tests/ui/privacy/struct-field-and-impl-expose-10545.stderr @@ -1,11 +1,11 @@ error[E0603]: struct `S` is private - --> $DIR/issue-10545.rs:6:14 + --> $DIR/struct-field-and-impl-expose-10545.rs:8:14 | LL | fn foo(_: a::S) { | ^ private struct | note: the struct `S` is defined here - --> $DIR/issue-10545.rs:2:5 + --> $DIR/struct-field-and-impl-expose-10545.rs:4:5 | LL | struct S; | ^^^^^^^^^ diff --git a/tests/ui/structs/mutable-unit-struct-borrow.rs b/tests/ui/structs/mutable-unit-struct-borrow-11267.rs similarity index 82% rename from tests/ui/structs/mutable-unit-struct-borrow.rs rename to tests/ui/structs/mutable-unit-struct-borrow-11267.rs index 036ad1d54edc..d96c4a4e79bc 100644 --- a/tests/ui/structs/mutable-unit-struct-borrow.rs +++ b/tests/ui/structs/mutable-unit-struct-borrow-11267.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11267 + //@ run-pass // Tests that unary structs can be mutably borrowed. diff --git a/tests/ui/traits/blanket-impl-trait-object.rs b/tests/ui/traits/blanket-impl-trait-object-10456.rs similarity index 81% rename from tests/ui/traits/blanket-impl-trait-object.rs rename to tests/ui/traits/blanket-impl-trait-object-10456.rs index 51c740fd7293..f84214317746 100644 --- a/tests/ui/traits/blanket-impl-trait-object.rs +++ b/tests/ui/traits/blanket-impl-trait-object-10456.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10456 + //@ check-pass pub struct Foo; diff --git a/tests/ui/traits/missing-trait-method-error.rs b/tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.rs similarity index 80% rename from tests/ui/traits/missing-trait-method-error.rs rename to tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.rs index d899c3ffa912..d5a500900ff0 100644 --- a/tests/ui/traits/missing-trait-method-error.rs +++ b/tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/10465 + pub mod a { pub trait A { fn foo(&self); diff --git a/tests/ui/issues/issue-10465.stderr b/tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.stderr similarity index 88% rename from tests/ui/issues/issue-10465.stderr rename to tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.stderr index 0f46ebe505aa..ffd8fd39250d 100644 --- a/tests/ui/issues/issue-10465.stderr +++ b/tests/ui/traits/nested-mod-trait-method-lookup-leak-10465.stderr @@ -1,5 +1,5 @@ error[E0599]: no method named `foo` found for reference `&B` in the current scope - --> $DIR/issue-10465.rs:17:15 + --> $DIR/nested-mod-trait-method-lookup-leak-10465.rs:19:15 | LL | b.foo(); | ^^^ method not found in `&B` diff --git a/tests/ui/type-alias/mismatched-rc-foo-types.rs b/tests/ui/type-alias/dummy-binder-102964.rs similarity index 75% rename from tests/ui/type-alias/mismatched-rc-foo-types.rs rename to tests/ui/type-alias/dummy-binder-102964.rs index 43ff23600766..6b6fa3ed5e33 100644 --- a/tests/ui/type-alias/mismatched-rc-foo-types.rs +++ b/tests/ui/type-alias/dummy-binder-102964.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/102964 + use std::rc::Rc; type Foo<'a, T> = &'a dyn Fn(&T); type RcFoo<'a, T> = Rc>; diff --git a/tests/ui/issues/issue-102964.stderr b/tests/ui/type-alias/dummy-binder-102964.stderr similarity index 93% rename from tests/ui/issues/issue-102964.stderr rename to tests/ui/type-alias/dummy-binder-102964.stderr index 0e2761f3f57b..fc32cabaf71a 100644 --- a/tests/ui/issues/issue-102964.stderr +++ b/tests/ui/type-alias/dummy-binder-102964.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-102964.rs:5:41 + --> $DIR/dummy-binder-102964.rs:7:41 | LL | fn bar_function(function: Foo) -> RcFoo { | ------------ ^^^^^^^^ expected `Rc<&dyn Fn(&T)>`, found `()` diff --git a/tests/ui/type-alias/static-method-type-alias.rs b/tests/ui/type-alias/static-method-type-alias-11047.rs similarity index 87% rename from tests/ui/type-alias/static-method-type-alias.rs rename to tests/ui/type-alias/static-method-type-alias-11047.rs index 6e1b2856afcd..efb336fb4f76 100644 --- a/tests/ui/type-alias/static-method-type-alias.rs +++ b/tests/ui/type-alias/static-method-type-alias-11047.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11047 + //@ run-pass // Test that static methods can be invoked on `type` aliases diff --git a/tests/ui/unsafe/raw-pointer-field-access-error.rs b/tests/ui/unsafe/raw-pointer-field-access-error.rs index 0c34554c12d1..04b45b2d3c68 100644 --- a/tests/ui/unsafe/raw-pointer-field-access-error.rs +++ b/tests/ui/unsafe/raw-pointer-field-access-error.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/11004 + use std::mem; struct A { x: i32, y: f64 } diff --git a/tests/ui/issues/issue-11004.stderr b/tests/ui/unsafe/raw-pointer-field-access-error.stderr similarity index 85% rename from tests/ui/issues/issue-11004.stderr rename to tests/ui/unsafe/raw-pointer-field-access-error.stderr index 6d157c911302..e9a205a5fa64 100644 --- a/tests/ui/issues/issue-11004.stderr +++ b/tests/ui/unsafe/raw-pointer-field-access-error.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `x` on type `*mut A` - --> $DIR/issue-11004.rs:7:21 + --> $DIR/raw-pointer-field-access-error.rs:9:21 | LL | let x : i32 = n.x; | ^ unknown field @@ -10,7 +10,7 @@ LL | let x : i32 = (*n).x; | ++ + error[E0609]: no field `y` on type `*mut A` - --> $DIR/issue-11004.rs:8:21 + --> $DIR/raw-pointer-field-access-error.rs:10:21 | LL | let y : f64 = n.y; | ^ unknown field From a076bd8a61841941f52a43a31c9c77def961227e Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 25 Jul 2025 17:08:57 -0500 Subject: [PATCH 060/201] Remove `compiler-builtins-no-asm` This feature used to be for when Cranelift didn't support inline assembly, but its last uses were removed in 52933e0bd200 ("Don't disable inline asm usage in compiler-builtins when the cranelift backend is enabled"). and cba05a7a14b3 ("Support naked functions"). This doesn't remove the feature from the `compiler-builtins` crate, that will be done separately in the subtree repo. --- library/alloc/Cargo.toml | 1 - library/std/Cargo.toml | 1 - library/sysroot/Cargo.toml | 1 - 3 files changed, 3 deletions(-) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 017c790ecac3..49f5c42d1e13 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -21,7 +21,6 @@ compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features [features] compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] -compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] # Make panics and failed asserts immediately abort without formatting any message diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index ba1e1f5218af..7015b707759c 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -97,7 +97,6 @@ backtrace-trace-only = [] panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] -compiler-builtins-no-asm = ["alloc/compiler-builtins-no-asm"] compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] llvm-libunwind = ["unwind/llvm-libunwind"] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 032f5272a9cd..4a151a4264d7 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -23,7 +23,6 @@ backtrace = ["std/backtrace"] backtrace-trace-only = ["std/backtrace-trace-only"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] -compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] debug_refcell = ["std/debug_refcell"] From 51c5f191dec8b78b8a1b6e26eec26c26556125e3 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Fri, 25 Jul 2025 19:21:20 -0500 Subject: [PATCH 061/201] Remove `compiler-builtins-mangled-names` This config was added in 207de019dc67 ("libary: Forward compiler-builtins "asm" and "mangled-names" feature") but it does not appear this has ever been used. The PR adding it (RUST-78472) says that this was exposed to help with configuration and points at the [Hermit Cargo config], but as far as I can tell, this feature name has never been mentioned in that repository's git history. Thus, clean up a seemingly unneeded feature. [Hermit Cargo config]: https://github.com/hermit-os/hermit-rs/blob/ab2b830930e6a9a98c8294997a8183feeabeda4a/.cargo/config --- library/alloc/Cargo.toml | 1 - library/std/Cargo.toml | 1 - library/sysroot/Cargo.toml | 1 - 3 files changed, 3 deletions(-) diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml index 49f5c42d1e13..9ba7c5bd28a1 100644 --- a/library/alloc/Cargo.toml +++ b/library/alloc/Cargo.toml @@ -22,7 +22,6 @@ compiler_builtins = { path = "../compiler-builtins/compiler-builtins", features compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] compiler-builtins-no-f16-f128 = ["compiler_builtins/no-f16-f128"] -compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] # Make panics and failed asserts immediately abort without formatting any message panic_immediate_abort = ["core/panic_immediate_abort"] # Choose algorithms that are optimized for binary size instead of runtime performance diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7015b707759c..d77f742e11b6 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -98,7 +98,6 @@ panic-unwind = ["dep:panic_unwind"] compiler-builtins-c = ["alloc/compiler-builtins-c"] compiler-builtins-mem = ["alloc/compiler-builtins-mem"] compiler-builtins-no-f16-f128 = ["alloc/compiler-builtins-no-f16-f128"] -compiler-builtins-mangled-names = ["alloc/compiler-builtins-mangled-names"] llvm-libunwind = ["unwind/llvm-libunwind"] system-llvm-libunwind = ["unwind/system-llvm-libunwind"] diff --git a/library/sysroot/Cargo.toml b/library/sysroot/Cargo.toml index 4a151a4264d7..82b93682c610 100644 --- a/library/sysroot/Cargo.toml +++ b/library/sysroot/Cargo.toml @@ -24,7 +24,6 @@ backtrace-trace-only = ["std/backtrace-trace-only"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] compiler-builtins-no-f16-f128 = ["std/compiler-builtins-no-f16-f128"] -compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] debug_refcell = ["std/debug_refcell"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] From 7f7d343400de843c12f880b02ea1a7b22ccc7379 Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:35:53 -0400 Subject: [PATCH 062/201] str: Mark unstable `round_char_boundary` feature functions as const Mark `floor_char_boundary`, `ceil_char_boundary` const Simplify the implementations, reducing the number of arithmetic operations --- library/core/src/str/mod.rs | 38 ++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 029abf175391..c40af4de7e03 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -407,17 +407,22 @@ impl str { /// ``` #[unstable(feature = "round_char_boundary", issue = "93743")] #[inline] - pub fn floor_char_boundary(&self, index: usize) -> usize { + pub const fn floor_char_boundary(&self, index: usize) -> usize { if index >= self.len() { self.len() } else { - let lower_bound = index.saturating_sub(3); - let new_index = self.as_bytes()[lower_bound..=index] - .iter() - .rposition(|b| b.is_utf8_char_boundary()); + let mut i = index; + while i > 0 { + if self.as_bytes()[i].is_utf8_char_boundary() { + break; + } + i -= 1; + } - // SAFETY: we know that the character boundary will be within four bytes - unsafe { lower_bound + new_index.unwrap_unchecked() } + // The character boundary will be within four bytes of the index + debug_assert!(i >= index.saturating_sub(3)); + + i } } @@ -445,15 +450,22 @@ impl str { /// ``` #[unstable(feature = "round_char_boundary", issue = "93743")] #[inline] - pub fn ceil_char_boundary(&self, index: usize) -> usize { + pub const fn ceil_char_boundary(&self, index: usize) -> usize { if index >= self.len() { self.len() } else { - let upper_bound = Ord::min(index + 4, self.len()); - self.as_bytes()[index..upper_bound] - .iter() - .position(|b| b.is_utf8_char_boundary()) - .map_or(upper_bound, |pos| pos + index) + let mut i = index; + while i < self.len() { + if self.as_bytes()[i].is_utf8_char_boundary() { + break; + } + i += 1; + } + + // The character boundary will be within four bytes of the index + debug_assert!(i <= index + 3); + + i } } From e39201395d5626feefe669c9499667b927f29a58 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sat, 26 Jul 2025 11:57:48 +0800 Subject: [PATCH 063/201] fix fold doc comment for multiline param list fn --- .../rust-analyzer/crates/ide/src/folding_ranges.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index 698fd147789f..c00b3e83d3c3 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -73,11 +73,13 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec { } if fn_node.body().is_some() { + // Get the actual start of the function (excluding doc comments) + let fn_start = fn_node + .fn_token() + .map(|token| token.text_range().start()) + .unwrap_or(node.text_range().start()); res.push(Fold { - range: TextRange::new( - node.text_range().start(), - node.text_range().end(), - ), + range: TextRange::new(fn_start, node.text_range().end()), kind: FoldKind::Function, }); continue; From 2c29977dfc97e295831c960d11b1740dfb74358d Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sat, 26 Jul 2025 11:59:50 +0800 Subject: [PATCH 064/201] test: add test case for fold doc comment for multiline param list fn --- .../crates/ide/src/folding_ranges.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index c00b3e83d3c3..b06b3f82e36c 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -690,4 +690,21 @@ type Foo = foo< "#, ) } + + #[test] + fn test_fold_doc_comments_with_multiline_paramlist_function() { + check( + r#" +/// A very very very very very very very very very very very very very very very +/// very very very long description +fn foo( + very_long_parameter_name: u32, + another_very_long_parameter_name: u32, + third_very_long_parm: u32, +) { + todo!() +} +"#, + ); + } } From 9e9ff76e32154be182ea2a8489249d6212f520e9 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 26 Jul 2025 13:26:11 +0900 Subject: [PATCH 065/201] add Debug on AstSubst PathTransform.rs Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 0ab880bcfe71..36e28dfa4c56 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -15,12 +15,13 @@ use syntax::{ ted, }; -#[derive(Default)] +#[derive(Default, Debug)] struct AstSubsts { types_and_consts: Vec, lifetimes: Vec, } +#[derive(Debug)] enum TypeOrConst { Either(ast::TypeArg), // indistinguishable type or const param Const(ast::ConstArg), From d70e67b604a551d240a38fb05f12c543f443960d Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 26 Jul 2025 13:26:39 +0900 Subject: [PATCH 066/201] Migrate PathTransform to SyntaxEditor Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../extract_struct_from_enum_variant.rs | 29 +++-- .../src/handlers/generate_delegate_methods.rs | 8 +- .../src/handlers/generate_delegate_trait.rs | 50 ++++---- .../src/handlers/generate_function.rs | 29 +++-- .../ide-assists/src/handlers/inline_call.rs | 9 +- .../crates/ide-assists/src/utils.rs | 9 +- .../src/completions/item_list/trait_impl.rs | 4 +- .../crates/ide-db/src/path_transform.rs | 116 ++++++++++++------ 8 files changed, 168 insertions(+), 86 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index b9c42285d257..9095b1825f5f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -16,8 +16,9 @@ use syntax::{ SyntaxKind::*, SyntaxNode, T, ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::IndentLevel, - edit_in_place::Indent, make, + self, AstNode, HasAttrs, HasGenericParams, HasName, HasVisibility, + edit::{AstNodeEdit, IndentLevel}, + make, }, match_ast, ted, }; @@ -110,20 +111,30 @@ pub(crate) fn extract_struct_from_enum_variant( let generics = generic_params.as_ref().map(|generics| generics.clone_for_update()); // resolve GenericArg in field_list to actual type - let field_list = field_list.clone_for_update(); - if let Some((target_scope, source_scope)) = + let field_list = if let Some((target_scope, source_scope)) = ctx.sema.scope(enum_ast.syntax()).zip(ctx.sema.scope(field_list.syntax())) { - PathTransform::generic_transformation(&target_scope, &source_scope) - .apply(field_list.syntax()); - } + let field_list = field_list.reset_indent(); + let field_list = + PathTransform::generic_transformation(&target_scope, &source_scope) + .apply(field_list.syntax()); + match_ast! { + match field_list { + ast::RecordFieldList(field_list) => Either::Left(field_list), + ast::TupleFieldList(field_list) => Either::Right(field_list), + _ => unreachable!(), + } + } + } else { + field_list.clone_for_update() + }; let def = create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast); let enum_ast = variant.parent_enum(); let indent = enum_ast.indent_level(); - def.reindent_to(indent); + let def = def.indent(indent); ted::insert_all( ted::Position::before(enum_ast.syntax()), @@ -279,7 +290,7 @@ fn create_struct_def( field_list.clone().into() } }; - field_list.reindent_to(IndentLevel::single()); + let field_list = field_list.indent(IndentLevel::single()); let strukt = make::struct_(enum_vis, name, generics, field_list).clone_for_update(); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs index ca66cb69dcc0..606389807604 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_methods.rs @@ -114,9 +114,13 @@ pub(crate) fn generate_delegate_methods(acc: &mut Assists, ctx: &AssistContext<' let source_scope = ctx.sema.scope(v.syntax()); let target_scope = ctx.sema.scope(strukt.syntax()); if let (Some(s), Some(t)) = (source_scope, target_scope) { - PathTransform::generic_transformation(&t, &s).apply(v.syntax()); + ast::Fn::cast( + PathTransform::generic_transformation(&t, &s).apply(v.syntax()), + ) + .unwrap_or(v) + } else { + v } - v } None => return, }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index 2bdc85e785e9..e96250f3c50a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -255,7 +255,6 @@ fn generate_impl( delegee: &Delegee, edition: Edition, ) -> Option { - let delegate: ast::Impl; let db = ctx.db(); let ast_strukt = &strukt.strukt; let strukt_ty = make::ty_path(make::ext::ident_path(&strukt.name.to_string())); @@ -266,7 +265,7 @@ fn generate_impl( let bound_def = ctx.sema.source(delegee.to_owned())?.value; let bound_params = bound_def.generic_param_list(); - delegate = make::impl_trait( + let delegate = make::impl_trait( delegee.is_unsafe(db), bound_params.clone(), bound_params.map(|params| params.to_generic_args()), @@ -304,7 +303,7 @@ fn generate_impl( 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()); + ast::Impl::cast(transform.apply(delegate.syntax())) } Delegee::Impls(trait_, old_impl) => { let old_impl = ctx.sema.source(old_impl.to_owned())?.value; @@ -358,20 +357,28 @@ fn generate_impl( // 2.3) Instantiate generics with `transform_impl`, this step also // remove unused params. - let mut trait_gen_args = old_impl.trait_()?.generic_arg_list(); - if let Some(trait_args) = &mut trait_gen_args { - *trait_args = trait_args.clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, trait_args.syntax())?; - } + let trait_gen_args = old_impl.trait_()?.generic_arg_list().and_then(|trait_args| { + let trait_args = &mut trait_args.clone_for_update(); + if let Some(new_args) = transform_impl( + ctx, + ast_strukt, + &old_impl, + &transform_args, + trait_args.clone_subtree(), + ) { + *trait_args = new_args.clone_subtree(); + Some(new_args) + } else { + None + } + }); let type_gen_args = strukt_params.clone().map(|params| params.to_generic_args()); - let path_type = make::ty(&trait_.name(db).display_no_db(edition).to_smolstr()).clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type.syntax())?; - + let path_type = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, path_type)?; // 3) Generate delegate trait impl - delegate = make::impl_trait( + let delegate = make::impl_trait( trait_.is_unsafe(db), trait_gen_params, trait_gen_args, @@ -385,7 +392,6 @@ fn generate_impl( None, ) .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, delegate.trait_()?)); @@ -398,7 +404,7 @@ fn generate_impl( .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) { let item = item.clone_for_update(); - transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item.syntax())?; + let item = transform_impl(ctx, ast_strukt, &old_impl, &transform_args, item)?; let assoc = process_assoc_item(item, qualified_path_type.clone(), field_name)?; delegate_assoc_items.add_item(assoc); @@ -408,19 +414,18 @@ fn generate_impl( if let Some(wc) = delegate.where_clause() { remove_useless_where_clauses(&delegate.trait_()?, &delegate.self_ty()?, wc); } + Some(delegate) } } - - Some(delegate) } -fn transform_impl( +fn transform_impl( ctx: &AssistContext<'_>, strukt: &ast::Struct, old_impl: &ast::Impl, args: &Option, - syntax: &syntax::SyntaxNode, -) -> Option<()> { + syntax: N, +) -> 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)?; @@ -437,8 +442,7 @@ fn transform_impl( }, ); - transform.apply(syntax); - Some(()) + N::cast(transform.apply(syntax.syntax())) } fn remove_instantiated_params( @@ -570,9 +574,7 @@ where let scope = ctx.sema.scope(item.syntax())?; let transform = PathTransform::adt_transformation(&scope, &scope, hir_adt, args.clone()); - transform.apply(item.syntax()); - - Some(item) + N::cast(transform.apply(item.syntax())) } fn has_self_type(trait_: hir::Trait, ctx: &AssistContext<'_>) -> Option<()> { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs index 78ae815dc87a..3290a70e1c69 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_function.rs @@ -743,17 +743,30 @@ fn fn_generic_params( let where_preds: Vec = where_preds.into_iter().map(|it| it.node.clone_for_update()).collect(); - // 4. Rewrite paths - if let Some(param) = generic_params.first() { - let source_scope = ctx.sema.scope(param.syntax())?; - let target_scope = ctx.sema.scope(&target.parent())?; - if source_scope.module() != target_scope.module() { + let (generic_params, where_preds): (Vec, Vec) = + if let Some(param) = generic_params.first() + && let source_scope = ctx.sema.scope(param.syntax())? + && let target_scope = ctx.sema.scope(&target.parent())? + && source_scope.module() != target_scope.module() + { + // 4. Rewrite paths let transform = PathTransform::generic_transformation(&target_scope, &source_scope); let generic_params = generic_params.iter().map(|it| it.syntax()); let where_preds = where_preds.iter().map(|it| it.syntax()); - transform.apply_all(generic_params.chain(where_preds)); - } - } + transform + .apply_all(generic_params.chain(where_preds)) + .into_iter() + .filter_map(|it| { + if let Some(it) = ast::GenericParam::cast(it.clone()) { + Some(either::Either::Left(it)) + } else { + ast::WherePred::cast(it).map(either::Either::Right) + } + }) + .partition_map(|it| it) + } else { + (generic_params, where_preds) + }; let generic_param_list = make::generic_param_list(generic_params); let where_clause = diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index b7b8bc604a51..1549b414dcc1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -537,8 +537,13 @@ fn inline( if let Some(generic_arg_list) = generic_arg_list.clone() { if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { - PathTransform::function_call(target, source, function, generic_arg_list) - .apply(body.syntax()); + body.reindent_to(IndentLevel(0)); + if let Some(new_body) = ast::BlockExpr::cast( + PathTransform::function_call(target, source, function, generic_arg_list) + .apply(body.syntax()), + ) { + body = new_body; + } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 3457367645f0..12fff32be038 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -191,7 +191,7 @@ pub fn add_trait_assoc_items_to_impl( original_items .iter() .map(|InFile { file_id, value: original_item }| { - let cloned_item = { + let mut cloned_item = { if let Some(macro_file) = file_id.macro_file() { let span_map = sema.db.expansion_span_map(macro_file); let item_prettified = prettify_macro_expansion( @@ -207,17 +207,18 @@ pub fn add_trait_assoc_items_to_impl( } } original_item.clone_for_update() - }; + } + .reset_indent(); if let Some(source_scope) = sema.scope(original_item.syntax()) { // FIXME: Paths in nested macros are not handled well. See // `add_missing_impl_members::paths_in_nested_macro_should_get_transformed` test. let transform = PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); - transform.apply(cloned_item.syntax()); + cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap(); } cloned_item.remove_attrs_and_docs(); - cloned_item.reset_indent() + cloned_item }) .map(|item| { match &item { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 975c2f02259e..bcf8c0ec527a 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -276,7 +276,7 @@ fn get_transformed_assoc_item( let assoc_item = assoc_item.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. - transform.apply(assoc_item.syntax()); + let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?; assoc_item.remove_attrs_and_docs(); Some(assoc_item) } @@ -301,7 +301,7 @@ fn get_transformed_fn( let fn_ = fn_.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. - transform.apply(fn_.syntax()); + let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?; fn_.remove_attrs_and_docs(); match async_ { AsyncSugaring::Desugar => { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index 36e28dfa4c56..b7432d89c7b7 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -12,7 +12,7 @@ use span::Edition; use syntax::{ NodeOrToken, SyntaxNode, ast::{self, AstNode, HasGenericArgs, make}, - ted, + syntax_editor::{self, SyntaxEditor}, }; #[derive(Default, Debug)] @@ -129,15 +129,18 @@ impl<'a> PathTransform<'a> { } } - pub fn apply(&self, syntax: &SyntaxNode) { + #[must_use] + pub fn apply(&self, syntax: &SyntaxNode) -> SyntaxNode { self.build_ctx().apply(syntax) } - pub fn apply_all<'b>(&self, nodes: impl IntoIterator) { + #[must_use] + pub fn apply_all<'b>( + &self, + nodes: impl IntoIterator, + ) -> Vec { let ctx = self.build_ctx(); - for node in nodes { - ctx.apply(node); - } + nodes.into_iter().map(|node| ctx.apply(&node.clone())).collect() } fn prettify_target_node(&self, node: SyntaxNode) -> SyntaxNode { @@ -237,7 +240,7 @@ impl<'a> PathTransform<'a> { Some((k.name(db).display(db, target_edition).to_string(), v.lifetime()?)) }) .collect(); - let ctx = Ctx { + let mut ctx = Ctx { type_substs, const_substs, lifetime_substs, @@ -273,42 +276,75 @@ fn preorder_rev(item: &SyntaxNode) -> impl Iterator { } impl Ctx<'_> { - fn apply(&self, item: &SyntaxNode) { + fn apply(&self, item: &SyntaxNode) -> SyntaxNode { // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = preorder_rev(item).filter_map(ast::Path::cast).collect::>(); - for path in paths { - self.transform_path(path); - } - - preorder_rev(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + let item = self.transform_path(item).clone_subtree(); + let mut editor = SyntaxEditor::new(item.clone()); + preorder_rev(&item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { - ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); + editor + .replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); } }); + + editor.finish().new_root().clone() } - fn transform_default_values(&self, defaulted_params: Vec) { + fn transform_default_values(&mut self, defaulted_params: Vec) { // By now the default values are simply copied from where they are declared // and should be transformed. As any value is allowed to refer to previous // generic (both type and const) parameters, they should be all iterated left-to-right. for param in defaulted_params { - let value = match param { - Either::Left(k) => self.type_substs.get(&k).unwrap().syntax(), - Either::Right(k) => self.const_substs.get(&k).unwrap(), + let value = match ¶m { + Either::Left(k) => self.type_substs.get(k).unwrap().syntax(), + Either::Right(k) => self.const_substs.get(k).unwrap(), }; // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = preorder_rev(value).filter_map(ast::Path::cast).collect::>(); - for path in paths { - self.transform_path(path); + let new_value = self.transform_path(value); + match param { + Either::Left(k) => { + self.type_substs.insert(k, ast::Type::cast(new_value.clone()).unwrap()); + } + Either::Right(k) => { + self.const_substs.insert(k, new_value.clone()); + } } } } - fn transform_path(&self, path: ast::Path) -> Option<()> { + fn transform_path(&self, path: &SyntaxNode) -> SyntaxNode { + fn find_child_paths(root_path: &SyntaxNode) -> Vec { + let mut result = Vec::new(); + for child in root_path.children() { + if let Some(child_path) = ast::Path::cast(child.clone()) { + result.push(child_path); + } else { + result.extend(find_child_paths(&child)); + } + } + result + } + let root_path = path.clone_subtree(); + let result = find_child_paths(&root_path); + let mut editor = SyntaxEditor::new(root_path.clone()); + for sub_path in result { + let new = self.transform_path(sub_path.syntax()); + editor.replace(sub_path.syntax(), new); + } + let update_sub_item = editor.finish().new_root().clone().clone_subtree(); + let item = find_child_paths(&update_sub_item); + let mut editor = SyntaxEditor::new(update_sub_item); + for sub_path in item { + self.transform_path_(&mut editor, &sub_path); + } + editor.finish().new_root().clone() + } + + fn transform_path_(&self, editor: &mut SyntaxEditor, path: &ast::Path) -> Option<()> { if path.qualifier().is_some() { return None; } @@ -320,8 +356,7 @@ impl Ctx<'_> { // don't try to qualify sole `self` either, they are usually locals, but are returned as modules due to namespace clashing return None; } - - let resolution = self.source_scope.speculative_resolve(&path)?; + let resolution = self.source_scope.speculative_resolve(path)?; match resolution { hir::PathResolution::TypeParam(tp) => { @@ -361,12 +396,12 @@ impl Ctx<'_> { let segment = make::path_segment_ty(subst.clone(), trait_ref); let qualified = make::path_from_segments(std::iter::once(segment), false); - ted::replace(path.syntax(), qualified.clone_for_update().syntax()); + editor.replace(path.syntax(), qualified.clone_for_update().syntax()); } else if let Some(path_ty) = ast::PathType::cast(parent) { let old = path_ty.syntax(); if old.parent().is_some() { - ted::replace(old, subst.clone_subtree().clone_for_update().syntax()); + editor.replace(old, subst.clone_subtree().clone_for_update().syntax()); } else { // Some `path_ty` has no parent, especially ones made for default value // of type parameters. @@ -378,13 +413,13 @@ impl Ctx<'_> { } let start = path_ty.syntax().first_child().map(NodeOrToken::Node)?; let end = path_ty.syntax().last_child().map(NodeOrToken::Node)?; - ted::replace_all( + editor.replace_all( start..=end, new.syntax().children().map(NodeOrToken::Node).collect::>(), ); } } else { - ted::replace( + editor.replace( path.syntax(), subst.clone_subtree().clone_for_update().syntax(), ); @@ -410,17 +445,28 @@ impl Ctx<'_> { }; let found_path = self.target_module.find_path(self.source_scope.db, def, cfg)?; let res = mod_path_to_ast(&found_path, self.target_edition).clone_for_update(); + let mut res_editor = SyntaxEditor::new(res.syntax().clone_subtree()); if let Some(args) = path.segment().and_then(|it| it.generic_arg_list()) { if let Some(segment) = res.segment() { - let old = segment.get_or_create_generic_arg_list(); - ted::replace(old.syntax(), args.clone_subtree().syntax().clone_for_update()) + if let Some(old) = segment.generic_arg_list() { + res_editor.replace( + old.syntax(), + args.clone_subtree().syntax().clone_for_update(), + ) + } else { + res_editor.insert( + syntax_editor::Position::last_child_of(segment.syntax()), + args.clone_subtree().syntax().clone_for_update(), + ); + } } } - ted::replace(path.syntax(), res.syntax()) + let res = res_editor.finish().new_root().clone(); + editor.replace(path.syntax().clone(), res); } hir::PathResolution::ConstParam(cp) => { if let Some(subst) = self.const_substs.get(&cp) { - ted::replace(path.syntax(), subst.clone_subtree().clone_for_update()); + editor.replace(path.syntax(), subst.clone_subtree().clone_for_update()); } } hir::PathResolution::SelfType(imp) => { @@ -457,13 +503,13 @@ impl Ctx<'_> { mod_path_to_ast(&found_path, self.target_edition).qualifier() { let res = make::path_concat(qual, path_ty.path()?).clone_for_update(); - ted::replace(path.syntax(), res.syntax()); + editor.replace(path.syntax(), res.syntax()); return Some(()); } } } - ted::replace(path.syntax(), ast_ty.syntax()); + editor.replace(path.syntax(), ast_ty.syntax()); } hir::PathResolution::Local(_) | hir::PathResolution::Def(_) From d88bdc9927ade8747484a9a8e80ac55a63dbcc40 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 26 Jul 2025 09:38:38 +0200 Subject: [PATCH 067/201] CI: run apt update before installing anything --- src/tools/miri/.github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index e2e81370c600..c47f9695624b 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -52,6 +52,10 @@ jobs: HOST_TARGET: ${{ matrix.host_target }} steps: - uses: actions/checkout@v4 + - name: apt update + if: ${{ startsWith(matrix.os, 'ubuntu') }} + # The runners seem to have outdated apt repos sometimes + run: sudo apt update - name: install qemu if: ${{ matrix.qemu }} run: sudo apt install qemu-user qemu-user-binfmt From 317cd5683c665faef50a6ff31cbb03aaeebe6f16 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 26 Jul 2025 17:07:41 +0900 Subject: [PATCH 068/201] Migrate part of utils.rs to use SyntaxEditor Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- src/tools/rust-analyzer/crates/ide-assists/src/utils.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 3457367645f0..7fbb0ef4925f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -27,6 +27,7 @@ use syntax::{ make, syntax_factory::SyntaxFactory, }, + syntax_editor::SyntaxEditor, ted, }; @@ -329,7 +330,7 @@ fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option Option { match expr { ast::Expr::BinExpr(bin) => { - let bin = bin.clone_for_update(); + let bin = bin.clone_subtree(); let op_token = bin.op_token()?; let rev_token = match op_token.kind() { T![==] => T![!=], @@ -345,8 +346,9 @@ fn invert_special_case_legacy(expr: &ast::Expr) -> Option { ); } }; - ted::replace(op_token, make::token(rev_token)); - Some(bin.into()) + let mut bin_editor = SyntaxEditor::new(bin.syntax().clone()); + bin_editor.replace(op_token, make::token(rev_token)); + ast::Expr::cast(bin_editor.finish().new_root().clone()) } ast::Expr::MethodCallExpr(mce) => { let receiver = mce.receiver()?; From 6ff9be827bad41d9ba95c222496d8ad06d05d4ae Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 26 Jul 2025 18:17:09 +0900 Subject: [PATCH 069/201] Migrate Convert_to_guarded_return to use SyntaxEditor Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../src/handlers/convert_to_guarded_return.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index bf1cfc74700d..2ea032fb62ba 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -13,7 +13,6 @@ use syntax::{ edit::{AstNodeEdit, IndentLevel}, make, }, - ted, }; use crate::{ @@ -132,7 +131,6 @@ fn if_expr_to_guarded_return( "Convert to guarded return", target, |edit| { - let if_expr = edit.make_mut(if_expr); let if_indent_level = IndentLevel::from_node(if_expr.syntax()); let replacement = match if_let_pat { None => { @@ -168,8 +166,9 @@ fn if_expr_to_guarded_return( .take_while(|i| *i != end_of_then), ) .collect(); - - ted::replace_with_many(if_expr.syntax(), then_statements) + let mut editor = edit.make_editor(if_expr.syntax()); + editor.replace_with_many(if_expr.syntax(), then_statements); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } @@ -214,7 +213,6 @@ fn let_stmt_to_guarded_return( "Convert to guarded return", target, |edit| { - let let_stmt = edit.make_mut(let_stmt); let let_indent_level = IndentLevel::from_node(let_stmt.syntax()); let replacement = { @@ -227,8 +225,9 @@ fn let_stmt_to_guarded_return( let let_else_stmt = let_else_stmt.indent(let_indent_level); let_else_stmt.syntax().clone() }; - - ted::replace(let_stmt.syntax(), replacement) + let mut editor = edit.make_editor(let_stmt.syntax()); + editor.replace(let_stmt.syntax(), replacement); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } From 23923682f007ec85bd618fff4af6c11d06302412 Mon Sep 17 00:00:00 2001 From: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> Date: Sat, 26 Jul 2025 18:23:22 +0900 Subject: [PATCH 070/201] Migrate `extract_expressions_from_format_string` assist to use `SyntaxEditor` Signed-off-by: Hayashi Mikihiro <34ttrweoewiwe28@gmail.com> --- .../extract_expressions_from_format_string.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs index 54699a9454f0..cdc0e967101a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_expressions_from_format_string.rs @@ -8,8 +8,7 @@ use syntax::{ AstNode, AstToken, NodeOrToken, SyntaxKind::WHITESPACE, T, - ast::{self, make}, - ted, + ast::{self, make, syntax_factory::SyntaxFactory}, }; // Assist: extract_expressions_from_format_string @@ -58,8 +57,6 @@ pub(crate) fn extract_expressions_from_format_string( "Extract format expressions", tt.syntax().text_range(), |edit| { - let tt = edit.make_mut(tt); - // Extract existing arguments in macro let tokens = tt.token_trees_and_tokens().collect_vec(); @@ -131,8 +128,10 @@ pub(crate) fn extract_expressions_from_format_string( } // Insert new args - let new_tt = make::token_tree(tt_delimiter, new_tt_bits).clone_for_update(); - ted::replace(tt.syntax(), new_tt.syntax()); + let make = SyntaxFactory::with_mappings(); + let new_tt = make.token_tree(tt_delimiter, new_tt_bits); + let mut editor = edit.make_editor(tt.syntax()); + editor.replace(tt.syntax(), new_tt.syntax()); if let Some(cap) = ctx.config.snippet_cap { // Add placeholder snippets over placeholder args @@ -145,15 +144,19 @@ pub(crate) fn extract_expressions_from_format_string( }; if stdx::always!(placeholder.kind() == T![_]) { - edit.add_placeholder_snippet_token(cap, placeholder); + let annotation = edit.make_placeholder_snippet(cap); + editor.add_annotation(placeholder, annotation); } } // Add the final tabstop after the format literal if let Some(NodeOrToken::Token(literal)) = new_tt.token_trees_and_tokens().nth(1) { - edit.add_tabstop_after_token(cap, literal); + let annotation = edit.make_tabstop_after(cap); + editor.add_annotation(literal, annotation); } } + editor.add_mappings(make.finish_with_mappings()); + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ); From 948f7798d70b6adb6a490b8867e155dadebd3f0e Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:03:07 +0000 Subject: [PATCH 071/201] Remove support for -Zcombine-cgu Nobody seems to actually use this, while still adding some extra complexity to the already rather complex codegen coordinator code. It is also not supported by any backend other than the LLVM backend. --- compiler/rustc_codegen_gcc/src/back/write.rs | 9 ---- compiler/rustc_codegen_gcc/src/lib.rs | 8 ---- compiler/rustc_codegen_llvm/src/back/write.rs | 23 ---------- compiler/rustc_codegen_llvm/src/lib.rs | 7 ---- compiler/rustc_codegen_ssa/src/back/write.rs | 42 +++---------------- .../rustc_codegen_ssa/src/traits/write.rs | 6 --- compiler/rustc_session/src/options.rs | 2 - 7 files changed, 6 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs index 113abe70805b..c1231142c658 100644 --- a/compiler/rustc_codegen_gcc/src/back/write.rs +++ b/compiler/rustc_codegen_gcc/src/back/write.rs @@ -4,7 +4,6 @@ use gccjit::{Context, OutputKind}; use rustc_codegen_ssa::back::link::ensure_removed; use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig}; use rustc_codegen_ssa::{CompiledModule, ModuleCodegen}; -use rustc_errors::DiagCtxtHandle; use rustc_fs_util::link_or_copy; use rustc_session::config::OutputType; use rustc_span::fatal_error::FatalError; @@ -258,14 +257,6 @@ pub(crate) fn codegen( )) } -pub(crate) fn link( - _cgcx: &CodegenContext, - _dcx: DiagCtxtHandle<'_>, - mut _modules: Vec>, -) -> Result, FatalError> { - unimplemented!(); -} - pub(crate) fn save_temp_bitcode( cgcx: &CodegenContext, _module: &ModuleCodegen, diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 71765c511381..a31206825007 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -426,14 +426,6 @@ impl WriteBackendMethods for GccCodegenBackend { fn serialize_module(_module: ModuleCodegen) -> (String, Self::ModuleBuffer) { unimplemented!(); } - - fn run_link( - cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, - modules: Vec>, - ) -> Result, FatalError> { - back::write::link(cgcx, dcx, modules) - } } /// This is the entrypoint for a hot plugged rustc_codegen_gccjit diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 6f8fba2a30dc..85a06f457ebe 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -796,29 +796,6 @@ pub(crate) fn optimize( Ok(()) } -pub(crate) fn link( - cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, - mut modules: Vec>, -) -> Result, FatalError> { - use super::lto::{Linker, ModuleBuffer}; - // Sort the modules by name to ensure deterministic behavior. - modules.sort_by(|a, b| a.name.cmp(&b.name)); - let (first, elements) = - modules.split_first().expect("Bug! modules must contain at least one module."); - - let mut linker = Linker::new(first.module_llvm.llmod()); - for module in elements { - let _timer = cgcx.prof.generic_activity_with_arg("LLVM_link_module", &*module.name); - let buffer = ModuleBuffer::new(module.module_llvm.llmod()); - linker - .add(buffer.data()) - .map_err(|()| llvm_err(dcx, LlvmError::SerializeModule { name: &module.name }))?; - } - drop(linker); - Ok(modules.remove(0)) -} - pub(crate) fn codegen( cgcx: &CodegenContext, module: ModuleCodegen, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 8b1913cfa756..ca84b6de8b11 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -168,13 +168,6 @@ impl WriteBackendMethods for LlvmCodegenBackend { let stats = llvm::build_string(|s| unsafe { llvm::LLVMRustPrintStatistics(s) }).unwrap(); print!("{stats}"); } - fn run_link( - cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, - modules: Vec>, - ) -> Result, FatalError> { - back::write::link(cgcx, dcx, modules) - } fn run_and_optimize_fat_lto( cgcx: &CodegenContext, exported_symbols_for_lto: &[String], diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index a41cbd306d0c..6773d3e24e94 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -797,10 +797,6 @@ pub(crate) enum WorkItemResult { /// The backend has finished compiling a CGU, nothing more required. Finished(CompiledModule), - /// The backend has finished compiling a CGU, which now needs linking - /// because `-Zcombine-cgu` was specified. - NeedsLink(ModuleCodegen), - /// The backend has finished compiling a CGU, which now needs to go through /// fat LTO. NeedsFatLto(FatLtoInput), @@ -884,7 +880,10 @@ fn execute_optimize_work_item( }; match lto_type { - ComputedLtoType::No => finish_intra_module_work(cgcx, module, module_config), + ComputedLtoType::No => { + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) + } ComputedLtoType::Thin => { let (name, thin_buffer) = B::prepare_thin(module, false); if let Some(path) = bitcode { @@ -1024,20 +1023,8 @@ fn execute_thin_lto_work_item( module_config: &ModuleConfig, ) -> Result, FatalError> { let module = B::optimize_thin(cgcx, module)?; - finish_intra_module_work(cgcx, module, module_config) -} - -fn finish_intra_module_work( - cgcx: &CodegenContext, - module: ModuleCodegen, - module_config: &ModuleConfig, -) -> Result, FatalError> { - if !cgcx.opts.unstable_opts.combine_cgu || module.kind == ModuleKind::Allocator { - let module = B::codegen(cgcx, module, module_config)?; - Ok(WorkItemResult::Finished(module)) - } else { - Ok(WorkItemResult::NeedsLink(module)) - } + let module = B::codegen(cgcx, module, module_config)?; + Ok(WorkItemResult::Finished(module)) } /// Messages sent to the coordinator. @@ -1343,7 +1330,6 @@ fn start_executing_work( // through codegen and LLVM. let mut compiled_modules = vec![]; let mut compiled_allocator_module = None; - let mut needs_link = Vec::new(); let mut needs_fat_lto = Vec::new(); let mut needs_thin_lto = Vec::new(); let mut lto_import_only_modules = Vec::new(); @@ -1625,7 +1611,6 @@ fn start_executing_work( Ok(WorkItemResult::Finished(compiled_module)) => { match compiled_module.kind { ModuleKind::Regular => { - assert!(needs_link.is_empty()); compiled_modules.push(compiled_module); } ModuleKind::Allocator => { @@ -1634,10 +1619,6 @@ fn start_executing_work( } } } - Ok(WorkItemResult::NeedsLink(module)) => { - assert!(compiled_modules.is_empty()); - needs_link.push(module); - } Ok(WorkItemResult::NeedsFatLto(fat_lto_input)) => { assert!(!started_lto); assert!(needs_thin_lto.is_empty()); @@ -1674,17 +1655,6 @@ fn start_executing_work( return Err(()); } - let needs_link = mem::take(&mut needs_link); - if !needs_link.is_empty() { - assert!(compiled_modules.is_empty()); - let dcx = cgcx.create_dcx(); - let dcx = dcx.handle(); - let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?; - let module = - B::codegen(&cgcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?; - compiled_modules.push(module); - } - // Drop to print timings drop(llvm_start_time); diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs index 8e78cbe1963b..f391c198e1a1 100644 --- a/compiler/rustc_codegen_ssa/src/traits/write.rs +++ b/compiler/rustc_codegen_ssa/src/traits/write.rs @@ -16,12 +16,6 @@ pub trait WriteBackendMethods: Clone + 'static { type ThinData: Send + Sync; type ThinBuffer: ThinBufferMethods; - /// Merge all modules into main_module and returning it - fn run_link( - cgcx: &CodegenContext, - dcx: DiagCtxtHandle<'_>, - modules: Vec>, - ) -> Result, FatalError>; /// Performs fat LTO by merging all modules into a single one, running autodiff /// if necessary and running any further optimizations fn run_and_optimize_fat_lto( diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 5f1973b31a1b..44b35e8921ec 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2168,8 +2168,6 @@ options! { "hash algorithm of source files used to check freshness in cargo (`blake3` or `sha256`)"), codegen_backend: Option = (None, parse_opt_string, [TRACKED], "the backend to use"), - combine_cgu: bool = (false, parse_bool, [TRACKED], - "combine CGUs into a single one"), contract_checks: Option = (None, parse_opt_bool, [TRACKED], "emit runtime checks for contract pre- and post-conditions (default: no)"), coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED], From 39235beeb41a8334a2a3d8e70c4baa10e681ed2b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 14:29:08 +0200 Subject: [PATCH 072/201] Add attributes to AttributeKind --- .../rustc_attr_data_structures/src/attributes.rs | 12 ++++++++++++ .../src/encode_cross_crate.rs | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 1e2576bef2c7..610c93a253ce 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -416,12 +416,24 @@ pub enum AttributeKind { /// Represents `#[pointee]` Pointee(Span), + /// Represents `#[proc_macro]` + ProcMacro(Span), + + /// Represents `#[proc_macro_attribute]` + ProcMacroAttribute(Span), + + /// Represents `#[proc_macro_derive]` + ProcMacroDerive { trait_name: Symbol, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). PubTransparent(Span), /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations). Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span }, + /// Represents `#[rustc_builtin_macro]`. + RustcBuiltinMacro { builtin_name: Option, helper_attrs: ThinVec, span: Span }, + /// Represents `#[rustc_layout_scalar_valid_range_end]`. RustcLayoutScalarValidRangeEnd(Box, Span), diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs index 159b807a3b2b..af2d46d0bc71 100644 --- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs +++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs @@ -61,8 +61,12 @@ impl AttributeKind { PassByValue(..) => Yes, Path(..) => No, Pointee(..) => No, + ProcMacro(..) => No, + ProcMacroAttribute(..) => No, + ProcMacroDerive { .. } => No, PubTransparent(..) => Yes, Repr { .. } => No, + RustcBuiltinMacro { .. } => Yes, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, RustcObjectLifetimeDefault => No, From c4e7cb806cde7a59bdfecacf8f3d1d6f89e1cdd4 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 14:29:21 +0200 Subject: [PATCH 073/201] Implement check_attr --- compiler/rustc_passes/src/check_attr.rs | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 4b524bb2bd27..0b329cc38b07 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -130,6 +130,22 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for attr in attrs { let mut style = None; match attr { + Attribute::Parsed(AttributeKind::ProcMacro(_)) => { + self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) + } + Attribute::Parsed(AttributeKind::ProcMacroAttribute(_)) => { + self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); + } + Attribute::Parsed(AttributeKind::ProcMacroDerive { span: attr_span, .. }) => { + self.check_generic_attr( + hir_id, + sym::proc_macro_derive, + *attr_span, + target, + Target::Fn, + ); + self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) + } Attribute::Parsed( AttributeKind::SkipDuringMethodDispatch { span: attr_span, .. } | AttributeKind::Coinductive(attr_span) @@ -275,6 +291,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::MacroTransparency(_) | AttributeKind::Pointee(..) | AttributeKind::Dummy + | AttributeKind::RustcBuiltinMacro { .. } | AttributeKind::OmitGdbPrettyPrinterSection, ) => { /* do nothing */ } Attribute::Parsed(AttributeKind::AsPtr(attr_span)) => { @@ -373,16 +390,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { [sym::should_panic, ..] => { self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn) } - [sym::proc_macro, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike) - } - [sym::proc_macro_attribute, ..] => { - self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute); - } - [sym::proc_macro_derive, ..] => { - self.check_generic_attr_unparsed(hir_id, attr, target, Target::Fn); - self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) - } [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } From 2f50c7807cab4e0b2c8fd2811733fdc58edd35b1 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 17:03:53 +0200 Subject: [PATCH 074/201] Parsers for the attributes Signed-off-by: Jonathan Brouwer --- .../rustc_attr_parsing/src/attributes/mod.rs | 1 + .../src/attributes/proc_macro_attrs.rs | 139 ++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 7 + 3 files changed, 147 insertions(+) create mode 100644 compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs index 15b90bd2ed72..0c10517d0440 100644 --- a/compiler/rustc_attr_parsing/src/attributes/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs @@ -41,6 +41,7 @@ pub(crate) mod must_use; pub(crate) mod no_implicit_prelude; pub(crate) mod non_exhaustive; pub(crate) mod path; +pub(crate) mod proc_macro_attrs; pub(crate) mod repr; pub(crate) mod rustc_internal; pub(crate) mod semantics; diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs new file mode 100644 index 000000000000..c8d591fa056f --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -0,0 +1,139 @@ +use rustc_attr_data_structures::AttributeKind; +use rustc_feature::{AttributeTemplate, template}; +use rustc_span::{Span, Symbol, sym}; +use thin_vec::ThinVec; + +use crate::attributes::{ + AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser, +}; +use crate::context::{AcceptContext, Stage}; +use crate::parser::ArgParser; + +pub(crate) struct ProcMacroParser; +impl NoArgsAttributeParser for ProcMacroParser { + const PATH: &[Symbol] = &[sym::proc_macro]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacro; +} + +pub(crate) struct ProcMacroAttributeParser; +impl NoArgsAttributeParser for ProcMacroAttributeParser { + const PATH: &[Symbol] = &[sym::proc_macro_attribute]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const CREATE: fn(Span) -> AttributeKind = AttributeKind::ProcMacroAttribute; +} + +pub(crate) struct ProcMacroDeriveParser; +impl SingleAttributeParser for ProcMacroDeriveParser { + const PATH: &[Symbol] = &[sym::proc_macro_derive]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = + template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let (trait_name, helper_attrs) = parse_derive_like(cx, args, true)?; + Some(AttributeKind::ProcMacroDerive { + trait_name: trait_name.expect("Trait name is mandatory, so it is present"), + helper_attrs, + span: cx.attr_span, + }) + } +} + +pub(crate) struct RustcBuiltinMacroParser; +impl SingleAttributeParser for RustcBuiltinMacroParser { + const PATH: &[Symbol] = &[sym::rustc_builtin_macro]; + const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const TEMPLATE: AttributeTemplate = + template!(List: "TraitName, /*opt*/ attributes(name1, name2, ...)"); + + fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option { + let (builtin_name, helper_attrs) = parse_derive_like(cx, args, false)?; + Some(AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, span: cx.attr_span }) + } +} + +fn parse_derive_like( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser<'_>, + trait_name_mandatory: bool, +) -> Option<(Option, ThinVec)> { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span); + return None; + }; + let mut items = list.mixed(); + + // Parse the name of the trait that is derived. + let Some(trait_attr) = items.next() else { + // For #[rustc_builtin_macro], it is permitted to leave out the trait name + if !trait_name_mandatory { + return None; + } + cx.expected_at_least_one_argument(list.span); + return None; + }; + let Some(trait_attr) = trait_attr.meta_item() else { + cx.unexpected_literal(trait_attr.span()); + return None; + }; + let Some(trait_ident) = trait_attr.path().word() else { + cx.expected_identifier(trait_attr.path().span()); + return None; + }; + if !trait_ident.name.can_be_raw() { + cx.expected_identifier(trait_ident.span); + return None; + } + if let Err(e) = trait_attr.args().no_args() { + cx.expected_no_args(e); + return None; + }; + + // Parse optional attributes + let mut attributes = ThinVec::new(); + if let Some(attrs) = items.next() { + let Some(attr_list) = attrs.meta_item() else { + cx.expected_list(attrs.span()); + return None; + }; + if !attr_list.path().word_is(sym::attributes) { + cx.expected_specific_argument(attrs.span(), vec!["attributes"]); + return None; + } + let Some(attr_list) = attr_list.args().list() else { + cx.expected_list(attrs.span()); + return None; + }; + + // Parse item in `attributes(...)` argument + for attr in attr_list.mixed() { + let Some(attr) = attr.meta_item() else { + cx.expected_identifier(attr.span()); + return None; + }; + if let Err(e) = attr.args().no_args() { + cx.expected_no_args(e); + return None; + }; + let Some(ident) = attr.path().word() else { + cx.expected_identifier(attr.path().span()); + return None; + }; + if !ident.name.can_be_raw() { + cx.expected_identifier(ident.span); + return None; + } + attributes.push(ident.name); + } + } + + // If anything else is specified, we should reject it + if let Some(next) = items.next() { + cx.expected_no_args(next.span()); + } + + Some((Some(trait_ident.name), attributes)) +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 45bfe3452071..9b86d1018408 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -38,6 +38,9 @@ use crate::attributes::must_use::MustUseParser; use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; use crate::attributes::non_exhaustive::NonExhaustiveParser; use crate::attributes::path::PathParser as PathAttributeParser; +use crate::attributes::proc_macro_attrs::{ + ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, +}; use crate::attributes::repr::{AlignParser, ReprParser}; use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart, @@ -154,6 +157,8 @@ attribute_parsers!( Single, Single, Single, + Single, + Single, Single, Single, Single, @@ -186,6 +191,8 @@ attribute_parsers!( Single>, Single>, Single>, + Single>, + Single>, Single>, Single>, Single>, From ec637000c6d5436b165a9b9ab4b008c036d22c99 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 20:48:42 +0200 Subject: [PATCH 075/201] Use the new attributes throughout the codebase Signed-off-by: Jonathan Brouwer --- .../src/attributes/proc_macro_attrs.rs | 8 +++--- .../src/proc_macro_harness.rs | 28 ++++++++++++++----- compiler/rustc_expand/src/base.rs | 26 +++++++++-------- compiler/rustc_hir/src/hir.rs | 11 ++++++++ compiler/rustc_metadata/src/rmeta/encoder.rs | 17 ++++------- compiler/rustc_middle/src/ty/mod.rs | 5 +++- 6 files changed, 60 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs index c8d591fa056f..4de77dc268ea 100644 --- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs @@ -61,6 +61,10 @@ fn parse_derive_like( trait_name_mandatory: bool, ) -> Option<(Option, ThinVec)> { let Some(list) = args.list() else { + // For #[rustc_builtin_macro], it is permitted to leave out the trait name + if args.no_args().is_ok() && !trait_name_mandatory { + return Some((None, ThinVec::new())); + } cx.expected_list(cx.attr_span); return None; }; @@ -68,10 +72,6 @@ fn parse_derive_like( // Parse the name of the trait that is derived. let Some(trait_attr) = items.next() else { - // For #[rustc_builtin_macro], it is permitted to leave out the trait name - if !trait_name_mandatory { - return None; - } cx.expected_at_least_one_argument(list.span); return None; }; diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs index 42b7e0e06d1f..09f5e6f6efc1 100644 --- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs +++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs @@ -1,11 +1,13 @@ -use std::mem; +use std::{mem, slice}; use rustc_ast::ptr::P; use rustc_ast::visit::{self, Visitor}; -use rustc_ast::{self as ast, NodeId, attr}; +use rustc_ast::{self as ast, HasNodeId, NodeId, attr}; use rustc_ast_pretty::pprust; +use rustc_attr_data_structures::AttributeKind; +use rustc_attr_parsing::AttributeParser; use rustc_errors::DiagCtxtHandle; -use rustc_expand::base::{ExtCtxt, ResolverExpand, parse_macro_name_and_helper_attrs}; +use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; use rustc_session::Session; @@ -22,7 +24,7 @@ struct ProcMacroDerive { trait_name: Symbol, function_ident: Ident, span: Span, - attrs: Vec, + attrs: ThinVec, } struct ProcMacroDef { @@ -41,6 +43,7 @@ struct CollectProcMacros<'a> { macros: Vec, in_root: bool, dcx: DiagCtxtHandle<'a>, + session: &'a Session, source_map: &'a SourceMap, is_proc_macro_crate: bool, is_test_crate: bool, @@ -63,6 +66,7 @@ pub fn inject( macros: Vec::new(), in_root: true, dcx, + session: sess, source_map: sess.source_map(), is_proc_macro_crate, is_test_crate, @@ -98,8 +102,18 @@ impl<'a> CollectProcMacros<'a> { function_ident: Ident, attr: &'a ast::Attribute, ) { - let Some((trait_name, proc_attrs)) = - parse_macro_name_and_helper_attrs(self.dcx, attr, "derive") + let Some(rustc_hir::Attribute::Parsed(AttributeKind::ProcMacroDerive { + trait_name, + helper_attrs, + .. + })) = AttributeParser::parse_limited( + self.session, + slice::from_ref(attr), + sym::proc_macro_derive, + item.span, + item.node_id(), + None, + ) else { return; }; @@ -110,7 +124,7 @@ impl<'a> CollectProcMacros<'a> { span: item.span, trait_name, function_ident, - attrs: proc_attrs, + attrs: helper_attrs, })); } else { let msg = if !self.in_root { diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 25ec54011111..ec2ca967ca5e 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -861,7 +861,7 @@ impl SyntaxExtension { /// | (unspecified) | no | if-ext | if-ext | yes | /// | external | no | if-ext | if-ext | yes | /// | yes | yes | yes | yes | yes | - fn get_collapse_debuginfo(sess: &Session, attrs: &[impl AttributeExt], ext: bool) -> bool { + fn get_collapse_debuginfo(sess: &Session, attrs: &[hir::Attribute], ext: bool) -> bool { let flag = sess.opts.cg.collapse_macro_debuginfo; let attr = ast::attr::find_by_name(attrs, sym::collapse_debuginfo) .and_then(|attr| { @@ -872,7 +872,7 @@ impl SyntaxExtension { .ok() }) .unwrap_or_else(|| { - if ast::attr::contains_name(attrs, sym::rustc_builtin_macro) { + if find_attr!(attrs, AttributeKind::RustcBuiltinMacro { .. }) { CollapseMacroDebuginfo::Yes } else { CollapseMacroDebuginfo::Unspecified @@ -915,16 +915,18 @@ impl SyntaxExtension { let collapse_debuginfo = Self::get_collapse_debuginfo(sess, attrs, !is_local); tracing::debug!(?name, ?local_inner_macros, ?collapse_debuginfo, ?allow_internal_unsafe); - let (builtin_name, helper_attrs) = ast::attr::find_by_name(attrs, sym::rustc_builtin_macro) - .map(|attr| { - // Override `helper_attrs` passed above if it's a built-in macro, - // marking `proc_macro_derive` macros as built-in is not a realistic use case. - parse_macro_name_and_helper_attrs(sess.dcx(), attr, "built-in").map_or_else( - || (Some(name), Vec::new()), - |(name, helper_attrs)| (Some(name), helper_attrs), - ) - }) - .unwrap_or_else(|| (None, helper_attrs)); + let (builtin_name, helper_attrs) = match find_attr!(attrs, AttributeKind::RustcBuiltinMacro { builtin_name, helper_attrs, .. } => (builtin_name, helper_attrs)) + { + // Override `helper_attrs` passed above if it's a built-in macro, + // marking `proc_macro_derive` macros as built-in is not a realistic use case. + Some((Some(name), helper_attrs)) => { + (Some(*name), helper_attrs.iter().copied().collect()) + } + Some((None, _)) => (Some(name), Vec::new()), + + // Not a built-in macro + None => (None, helper_attrs), + }; let stability = find_attr!(attrs, AttributeKind::Stability { stability, .. } => *stability); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e83f6a1df720..96501844264c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1363,6 +1363,17 @@ impl AttributeExt for Attribute { _ => None, } } + + fn is_proc_macro_attr(&self) -> bool { + matches!( + self, + Attribute::Parsed( + AttributeKind::ProcMacro(..) + | AttributeKind::ProcMacroAttribute(..) + | AttributeKind::ProcMacroDerive { .. } + ) + ) + } } // FIXME(fn_delegation): use function delegation instead of manually forwarding diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 24a3a6e0c4fc..ffb33c67f8ad 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -5,7 +5,7 @@ use std::io::{Read, Seek, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; -use rustc_attr_data_structures::EncodeCrossCrate; +use rustc_attr_data_structures::{AttributeKind, EncodeCrossCrate, find_attr}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::{Mmap, MmapMut}; use rustc_data_structures::sync::{join, par_for_each_in}; @@ -1965,18 +1965,13 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // Proc-macros may have attributes like `#[allow_internal_unstable]`, // so downstream crates need access to them. let attrs = tcx.hir_attrs(proc_macro); - let macro_kind = if ast::attr::contains_name(attrs, sym::proc_macro) { + let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) { MacroKind::Bang - } else if ast::attr::contains_name(attrs, sym::proc_macro_attribute) { + } else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) { MacroKind::Attr - } else if let Some(attr) = ast::attr::find_by_name(attrs, sym::proc_macro_derive) { - // This unwrap chain should have been checked by the proc-macro harness. - name = attr.meta_item_list().unwrap()[0] - .meta_item() - .unwrap() - .ident() - .unwrap() - .name; + } else if let Some(trait_name) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, ..} => trait_name) + { + name = *trait_name; MacroKind::Derive } else { bug!("Unknown proc-macro type for item {:?}", id); diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index a7cde2ad4854..4c95f0748d32 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2024,7 +2024,10 @@ impl<'tcx> TyCtxt<'tcx> { && let Some(def_id) = def_id.as_local() && let outer = self.def_span(def_id).ctxt().outer_expn_data() && matches!(outer.kind, ExpnKind::Macro(MacroKind::Derive, _)) - && self.has_attr(outer.macro_def_id.unwrap(), sym::rustc_builtin_macro) + && find_attr!( + self.get_all_attrs(outer.macro_def_id.unwrap()), + AttributeKind::RustcBuiltinMacro { .. } + ) { true } else { From fea7809edbd2ae0b3fc7720ef33a5e49a7a1ccd6 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 20:48:51 +0200 Subject: [PATCH 076/201] Update test results Signed-off-by: Jonathan Brouwer --- tests/ui/attributes/malformed-attrs.stderr | 45 +++-- .../unsafe/proc-unsafe-attributes.rs | 1 + .../unsafe/proc-unsafe-attributes.stderr | 50 +++--- .../issue-43106-gating-of-builtin-attrs.rs | 2 +- ...issue-43106-gating-of-builtin-attrs.stderr | 12 +- ...issue-43106-gating-of-proc_macro_derive.rs | 14 +- ...e-43106-gating-of-proc_macro_derive.stderr | 24 +-- tests/ui/proc-macro/attribute.rs | 51 ++++-- tests/ui/proc-macro/attribute.stderr | 158 +++++++++++------- tests/ui/proc-macro/invalid-attributes.rs | 24 ++- tests/ui/proc-macro/invalid-attributes.stderr | 63 ++++--- 11 files changed, 278 insertions(+), 166 deletions(-) diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 7ae24db8b5fe..814a1e5f6919 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -43,12 +43,6 @@ error: malformed `no_sanitize` attribute input LL | #[no_sanitize] | ^^^^^^^^^^^^^^ help: must be of the form: `#[no_sanitize(address, kcfi, memory, thread)]` -error: malformed `proc_macro` attribute input - --> $DIR/malformed-attrs.rs:100:1 - | -LL | #[proc_macro = 18] - | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]` - error: malformed `instruction_set` attribute input --> $DIR/malformed-attrs.rs:107:1 | @@ -67,18 +61,6 @@ error: malformed `coroutine` attribute input LL | #[coroutine = 63] || {} | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[coroutine]` -error: malformed `proc_macro_attribute` attribute input - --> $DIR/malformed-attrs.rs:117:1 - | -LL | #[proc_macro_attribute = 19] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]` - -error: malformed `proc_macro_derive` attribute input - --> $DIR/malformed-attrs.rs:124:1 - | -LL | #[proc_macro_derive] - | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` - error: malformed `must_not_suspend` attribute input --> $DIR/malformed-attrs.rs:133:1 | @@ -454,6 +436,24 @@ LL | #[no_implicit_prelude = 23] | | didn't expect any arguments here | help: must be of the form: `#[no_implicit_prelude]` +error[E0565]: malformed `proc_macro` attribute input + --> $DIR/malformed-attrs.rs:100:1 + | +LL | #[proc_macro = 18] + | ^^^^^^^^^^^^^----^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro]` + +error[E0565]: malformed `proc_macro_attribute` attribute input + --> $DIR/malformed-attrs.rs:117:1 + | +LL | #[proc_macro_attribute = 19] + | ^^^^^^^^^^^^^^^^^^^^^^^----^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_attribute]` + error[E0539]: malformed `must_use` attribute input --> $DIR/malformed-attrs.rs:120:1 | @@ -471,6 +471,15 @@ LL - #[must_use = 1] LL + #[must_use] | +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/malformed-attrs.rs:124:1 + | +LL | #[proc_macro_derive] + | ^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input --> $DIR/malformed-attrs.rs:129:1 | diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs index 2f17d9620b41..bd136e64d3f9 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.rs @@ -12,6 +12,7 @@ pub fn b() {} #[proc_macro_derive(unsafe(Foo))] //~^ ERROR attribute is only usable with crates of the `proc-macro` crate type //~| ERROR: expected identifier, found keyword `unsafe` +//~| ERROR malformed `proc_macro_derive` attribute input pub fn c() {} #[unsafe(proc_macro_attribute)] diff --git a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr index 25b83a26e170..884e7663c856 100644 --- a/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr +++ b/tests/ui/attributes/unsafe/proc-unsafe-attributes.stderr @@ -1,11 +1,11 @@ error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument @@ -40,7 +40,7 @@ LL | #[proc_macro_derive(r#unsafe(Foo))] | ++ error: `proc_macro_attribute` is not an unsafe attribute - --> $DIR/proc-unsafe-attributes.rs:17:3 + --> $DIR/proc-unsafe-attributes.rs:18:3 | LL | #[unsafe(proc_macro_attribute)] | ^^^^^^ this is not an unsafe attribute @@ -48,7 +48,7 @@ LL | #[unsafe(proc_macro_attribute)] = note: extraneous unsafe is not allowed in attributes error: `allow` is not an unsafe attribute - --> $DIR/proc-unsafe-attributes.rs:22:3 + --> $DIR/proc-unsafe-attributes.rs:23:3 | LL | #[unsafe(allow(dead_code))] | ^^^^^^ this is not an unsafe attribute @@ -56,7 +56,7 @@ LL | #[unsafe(allow(dead_code))] = note: extraneous unsafe is not allowed in attributes error: `allow` is not an unsafe attribute - --> $DIR/proc-unsafe-attributes.rs:26:3 + --> $DIR/proc-unsafe-attributes.rs:27:3 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^ this is not an unsafe attribute @@ -64,7 +64,7 @@ LL | #[unsafe(allow(unsafe(dead_code)))] = note: extraneous unsafe is not allowed in attributes error: expected identifier, found keyword `unsafe` - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^ expected identifier, found keyword @@ -93,13 +93,13 @@ LL | #[proc_macro_derive(unsafe(Foo))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/proc-unsafe-attributes.rs:17:1 + --> $DIR/proc-unsafe-attributes.rs:18:1 | LL | #[unsafe(proc_macro_attribute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument @@ -107,7 +107,24 @@ LL | #[unsafe(allow(unsafe(dead_code)))] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 + | +LL | #[unsafe(allow(unsafe(dead_code)))] + | ^^^^^^^^^^^^^^^^^ bad attribute argument + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/proc-unsafe-attributes.rs:12:1 + | +LL | #[proc_macro_derive(unsafe(Foo))] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^-----^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + +error[E0452]: malformed lint attribute input + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument @@ -115,21 +132,14 @@ LL | #[unsafe(allow(unsafe(dead_code)))] = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 + --> $DIR/proc-unsafe-attributes.rs:27:16 | LL | #[unsafe(allow(unsafe(dead_code)))] | ^^^^^^^^^^^^^^^^^ bad attribute argument | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0452]: malformed lint attribute input - --> $DIR/proc-unsafe-attributes.rs:26:16 - | -LL | #[unsafe(allow(unsafe(dead_code)))] - | ^^^^^^^^^^^^^^^^^ bad attribute argument - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: aborting due to 18 previous errors -error: aborting due to 17 previous errors - -For more information about this error, try `rustc --explain E0452`. +Some errors have detailed explanations: E0452, E0565. +For more information about an error, try `rustc --explain E0452`. diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs index 9740eaaf1e93..b93cb2ea006d 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs @@ -57,7 +57,7 @@ // see gated-link-args.rs // see issue-43106-gating-of-macro_escape.rs for crate-level; but non crate-level is below at "2700" // (cannot easily test gating of crate-level #[no_std]; but non crate-level is below at "2600") -#![proc_macro_derive()] //~ WARN `#[proc_macro_derive]` only has an effect +#![proc_macro_derive(Test)] //~ WARN `#[proc_macro_derive]` only has an effect #![doc = "2400"] #![cold] //~ WARN attribute should be applied to a function //~^ WARN this was previously accepted diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr index 9016ca1efa76..8bac1f6155e8 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr @@ -367,12 +367,6 @@ warning: `#[should_panic]` only has an effect on functions LL | #![should_panic] | ^^^^^^^^^^^^^^^^ -warning: `#[proc_macro_derive]` only has an effect on functions - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1 - | -LL | #![proc_macro_derive()] - | ^^^^^^^^^^^^^^^^^^^^^^^ - warning: attribute should be applied to an `extern` block with non-Rust ABI --> $DIR/issue-43106-gating-of-builtin-attrs.rs:64:1 | @@ -409,6 +403,12 @@ warning: `#[must_use]` has no effect when applied to a module LL | #![must_use] | ^^^^^^^^^^^^ +warning: `#[proc_macro_derive]` only has an effect on functions + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:60:1 + | +LL | #![proc_macro_derive(Test)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + warning: attribute should be applied to a function definition --> $DIR/issue-43106-gating-of-builtin-attrs.rs:62:1 | diff --git a/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.rs b/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.rs index a94ffd602efb..392880e1b3b5 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.rs +++ b/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.rs @@ -7,27 +7,27 @@ // signal errors, making it incompatible with the "warnings only" // nature of issue-43106-gating-of-builtin-attrs.rs -#[proc_macro_derive()] +#[proc_macro_derive(Test)] //~^ ERROR the `#[proc_macro_derive]` attribute may only be used on bare functions mod proc_macro_derive1 { - mod inner { #![proc_macro_derive()] } + mod inner { #![proc_macro_derive(Test)] } // (no error issued here if there was one on outer module) } mod proc_macro_derive2 { - mod inner { #![proc_macro_derive()] } + mod inner { #![proc_macro_derive(Test)] } //~^ ERROR the `#[proc_macro_derive]` attribute may only be used on bare functions - #[proc_macro_derive()] fn f() { } + #[proc_macro_derive(Test)] fn f() { } //~^ ERROR the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` - #[proc_macro_derive()] struct S; + #[proc_macro_derive(Test)] struct S; //~^ ERROR the `#[proc_macro_derive]` attribute may only be used on bare functions - #[proc_macro_derive()] type T = S; + #[proc_macro_derive(Test)] type T = S; //~^ ERROR the `#[proc_macro_derive]` attribute may only be used on bare functions - #[proc_macro_derive()] impl S { } + #[proc_macro_derive(Test)] impl S { } //~^ ERROR the `#[proc_macro_derive]` attribute may only be used on bare functions } diff --git a/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.stderr b/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.stderr index e202b472d9ca..537032d777f5 100644 --- a/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.stderr +++ b/tests/ui/feature-gates/issue-43106-gating-of-proc_macro_derive.stderr @@ -1,38 +1,38 @@ error: the `#[proc_macro_derive]` attribute may only be used on bare functions --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:10:1 | -LL | #[proc_macro_derive()] - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(Test)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute may only be used on bare functions --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:18:17 | -LL | mod inner { #![proc_macro_derive()] } - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | mod inner { #![proc_macro_derive(Test)] } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:21:5 | -LL | #[proc_macro_derive()] fn f() { } - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(Test)] fn f() { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute may only be used on bare functions --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:24:5 | -LL | #[proc_macro_derive()] struct S; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(Test)] struct S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute may only be used on bare functions --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:27:5 | -LL | #[proc_macro_derive()] type T = S; - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(Test)] type T = S; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute may only be used on bare functions --> $DIR/issue-43106-gating-of-proc_macro_derive.rs:30:5 | -LL | #[proc_macro_derive()] impl S { } - | ^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(Test)] impl S { } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors diff --git a/tests/ui/proc-macro/attribute.rs b/tests/ui/proc-macro/attribute.rs index 30ed8ff82479..988cdcd04032 100644 --- a/tests/ui/proc-macro/attribute.rs +++ b/tests/ui/proc-macro/attribute.rs @@ -6,68 +6,85 @@ extern crate proc_macro; use proc_macro::*; -#[proc_macro_derive] //~ ERROR malformed `proc_macro_derive` attribute +#[proc_macro_derive] +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected this to be a list pub fn foo1(input: TokenStream) -> TokenStream { input } -#[proc_macro_derive = ""] //~ ERROR malformed `proc_macro_derive` attribute +#[proc_macro_derive = ""] +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected this to be a list pub fn foo2(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d3, a, b)] -//~^ ERROR attribute must have either one or two arguments +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE the only valid argument here is `attributes` pub fn foo3(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d4, attributes(a), b)] -//~^ ERROR attribute must have either one or two arguments +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect any arguments here pub fn foo4(input: TokenStream) -> TokenStream { input } #[proc_macro_derive("a")] -//~^ ERROR: not a meta item +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect a literal here pub fn foo5(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d6 = "")] -//~^ ERROR: must only be one word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect any arguments here pub fn foo6(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(m::d7)] -//~^ ERROR: must only be one word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected a valid identifier here pub fn foo7(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d8(a))] -//~^ ERROR: must only be one word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect any arguments here pub fn foo8(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(self)] -//~^ ERROR: `self` cannot be a name of derive macro +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected a valid identifier here pub fn foo9(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(PartialEq)] // OK pub fn foo10(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d11, a)] -//~^ ERROR: second argument must be `attributes` -//~| ERROR: attribute must be of form: `attributes(foo, bar)` +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE the only valid argument here is `attributes` pub fn foo11(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d12, attributes)] -//~^ ERROR: attribute must be of form: `attributes(foo, bar)` +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected this to be a list pub fn foo12(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d13, attributes("a"))] -//~^ ERROR: attribute must be a meta item, not a literal +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected a valid identifier here pub fn foo13(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d14, attributes(a = ""))] -//~^ ERROR: attribute must only be a single word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect any arguments here pub fn foo14(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d15, attributes(m::a))] -//~^ ERROR: attribute must only be a single word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected a valid identifier here pub fn foo15(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d16, attributes(a(b)))] -//~^ ERROR: attribute must only be a single word +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE didn't expect any arguments here pub fn foo16(input: TokenStream) -> TokenStream { input } #[proc_macro_derive(d17, attributes(self))] -//~^ ERROR: `self` cannot be a name of derive helper attribute +//~^ ERROR malformed `proc_macro_derive` attribute +//~| NOTE expected a valid identifier here pub fn foo17(input: TokenStream) -> TokenStream { input } diff --git a/tests/ui/proc-macro/attribute.stderr b/tests/ui/proc-macro/attribute.stderr index 3269aaf7f917..db59a1fdfb3d 100644 --- a/tests/ui/proc-macro/attribute.stderr +++ b/tests/ui/proc-macro/attribute.stderr @@ -1,104 +1,148 @@ -error: malformed `proc_macro_derive` attribute input +error[E0539]: malformed `proc_macro_derive` attribute input --> $DIR/attribute.rs:9:1 | LL | #[proc_macro_derive] - | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + | ^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: malformed `proc_macro_derive` attribute input - --> $DIR/attribute.rs:12:1 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:14:1 | LL | #[proc_macro_derive = ""] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must have either one or two arguments - --> $DIR/attribute.rs:15:1 - | -LL | #[proc_macro_derive(d3, a, b)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: attribute must have either one or two arguments +error[E0539]: malformed `proc_macro_derive` attribute input --> $DIR/attribute.rs:19:1 | -LL | #[proc_macro_derive(d4, attributes(a), b)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[proc_macro_derive(d3, a, b)] + | ^^^^^^^^^^^^^^^^^^^^^^^^-^^^^^ + | | | + | | the only valid argument here is `attributes` + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: not a meta item - --> $DIR/attribute.rs:23:21 +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:24:1 + | +LL | #[proc_macro_derive(d4, attributes(a), b)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` + +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:29:1 | LL | #[proc_macro_derive("a")] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^---^^ + | | | + | | didn't expect a literal here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: must only be one word - --> $DIR/attribute.rs:27:21 +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:34:1 | LL | #[proc_macro_derive(d6 = "")] - | ^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^----^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: must only be one word - --> $DIR/attribute.rs:31:21 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:39:1 | LL | #[proc_macro_derive(m::d7)] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^-----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: must only be one word - --> $DIR/attribute.rs:35:21 +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:44:1 | LL | #[proc_macro_derive(d8(a))] - | ^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^---^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: `self` cannot be a name of derive macro - --> $DIR/attribute.rs:39:21 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:49:1 | LL | #[proc_macro_derive(self)] - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^----^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: second argument must be `attributes` - --> $DIR/attribute.rs:46:26 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:57:1 | LL | #[proc_macro_derive(d11, a)] - | ^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^-^^ + | | | + | | the only valid argument here is `attributes` + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must be of form: `attributes(foo, bar)` - --> $DIR/attribute.rs:46:26 - | -LL | #[proc_macro_derive(d11, a)] - | ^ - -error: attribute must be of form: `attributes(foo, bar)` - --> $DIR/attribute.rs:51:26 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:62:1 | LL | #[proc_macro_derive(d12, attributes)] - | ^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^----------^^ + | | | + | | expected this to be a list + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must be a meta item, not a literal - --> $DIR/attribute.rs:55:37 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:67:1 | LL | #[proc_macro_derive(d13, attributes("a"))] - | ^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must only be a single word - --> $DIR/attribute.rs:59:37 +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:72:1 | LL | #[proc_macro_derive(d14, attributes(a = ""))] - | ^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must only be a single word - --> $DIR/attribute.rs:63:37 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:77:1 | LL | #[proc_macro_derive(d15, attributes(m::a))] - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: attribute must only be a single word - --> $DIR/attribute.rs:67:37 +error[E0565]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:82:1 | LL | #[proc_macro_derive(d16, attributes(a(b)))] - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: `self` cannot be a name of derive helper attribute - --> $DIR/attribute.rs:71:37 +error[E0539]: malformed `proc_macro_derive` attribute input + --> $DIR/attribute.rs:87:1 | LL | #[proc_macro_derive(d17, attributes(self))] - | ^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----^^^ + | | | + | | expected a valid identifier here + | help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]` -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors +Some errors have detailed explanations: E0539, E0565. +For more information about an error, try `rustc --explain E0539`. diff --git a/tests/ui/proc-macro/invalid-attributes.rs b/tests/ui/proc-macro/invalid-attributes.rs index a70c73e9b8f2..703defad69b2 100644 --- a/tests/ui/proc-macro/invalid-attributes.rs +++ b/tests/ui/proc-macro/invalid-attributes.rs @@ -7,20 +7,32 @@ extern crate proc_macro; use proc_macro::TokenStream; -#[proc_macro = "test"] //~ ERROR malformed `proc_macro` attribute +#[proc_macro = "test"] +//~^ ERROR malformed `proc_macro` attribute +//~| NOTE didn't expect any arguments here pub fn a(a: TokenStream) -> TokenStream { a } -#[proc_macro()] //~ ERROR malformed `proc_macro` attribute +#[proc_macro()] +//~^ ERROR malformed `proc_macro` attribute +//~| NOTE didn't expect any arguments here pub fn c(a: TokenStream) -> TokenStream { a } -#[proc_macro(x)] //~ ERROR malformed `proc_macro` attribute +#[proc_macro(x)] +//~^ ERROR malformed `proc_macro` attribute +//~| NOTE didn't expect any arguments here pub fn d(a: TokenStream) -> TokenStream { a } -#[proc_macro_attribute = "test"] //~ ERROR malformed `proc_macro_attribute` attribute +#[proc_macro_attribute = "test"] +//~^ ERROR malformed `proc_macro_attribute` attribute +//~| NOTE didn't expect any arguments here pub fn e(_: TokenStream, a: TokenStream) -> TokenStream { a } -#[proc_macro_attribute()] //~ ERROR malformed `proc_macro_attribute` attribute +#[proc_macro_attribute()] +//~^ ERROR malformed `proc_macro_attribute` attribute +//~| NOTE didn't expect any arguments here pub fn g(_: TokenStream, a: TokenStream) -> TokenStream { a } -#[proc_macro_attribute(x)] //~ ERROR malformed `proc_macro_attribute` attribute +#[proc_macro_attribute(x)] +//~^ ERROR malformed `proc_macro_attribute` attribute +//~| NOTE didn't expect any arguments here pub fn h(_: TokenStream, a: TokenStream) -> TokenStream { a } diff --git a/tests/ui/proc-macro/invalid-attributes.stderr b/tests/ui/proc-macro/invalid-attributes.stderr index fe411fa5e1f8..11c182ee03a1 100644 --- a/tests/ui/proc-macro/invalid-attributes.stderr +++ b/tests/ui/proc-macro/invalid-attributes.stderr @@ -1,38 +1,57 @@ -error: malformed `proc_macro` attribute input +error[E0565]: malformed `proc_macro` attribute input --> $DIR/invalid-attributes.rs:10:1 | LL | #[proc_macro = "test"] - | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]` + | ^^^^^^^^^^^^^--------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro]` -error: malformed `proc_macro` attribute input - --> $DIR/invalid-attributes.rs:13:1 +error[E0565]: malformed `proc_macro` attribute input + --> $DIR/invalid-attributes.rs:15:1 | LL | #[proc_macro()] - | ^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]` + | ^^^^^^^^^^^^--^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro]` -error: malformed `proc_macro` attribute input - --> $DIR/invalid-attributes.rs:16:1 +error[E0565]: malformed `proc_macro` attribute input + --> $DIR/invalid-attributes.rs:20:1 | LL | #[proc_macro(x)] - | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]` + | ^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro]` -error: malformed `proc_macro_attribute` attribute input - --> $DIR/invalid-attributes.rs:19:1 - | -LL | #[proc_macro_attribute = "test"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]` - -error: malformed `proc_macro_attribute` attribute input - --> $DIR/invalid-attributes.rs:22:1 - | -LL | #[proc_macro_attribute()] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]` - -error: malformed `proc_macro_attribute` attribute input +error[E0565]: malformed `proc_macro_attribute` attribute input --> $DIR/invalid-attributes.rs:25:1 | +LL | #[proc_macro_attribute = "test"] + | ^^^^^^^^^^^^^^^^^^^^^^^--------^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_attribute]` + +error[E0565]: malformed `proc_macro_attribute` attribute input + --> $DIR/invalid-attributes.rs:30:1 + | +LL | #[proc_macro_attribute()] + | ^^^^^^^^^^^^^^^^^^^^^^--^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_attribute]` + +error[E0565]: malformed `proc_macro_attribute` attribute input + --> $DIR/invalid-attributes.rs:35:1 + | LL | #[proc_macro_attribute(x)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]` + | ^^^^^^^^^^^^^^^^^^^^^^---^ + | | | + | | didn't expect any arguments here + | help: must be of the form: `#[proc_macro_attribute]` error: aborting due to 6 previous errors +For more information about this error, try `rustc --explain E0565`. From e64f75b3d26743d96690bac51b542409d97a9b2f Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 20:58:42 +0200 Subject: [PATCH 077/201] Remove now un-used code Signed-off-by: Jonathan Brouwer --- compiler/rustc_expand/messages.ftl | 27 ----------- compiler/rustc_expand/src/base.rs | 74 ----------------------------- compiler/rustc_expand/src/errors.rs | 66 ------------------------- 3 files changed, 167 deletions(-) diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 89d6e62834d6..5a53670c865d 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -1,26 +1,8 @@ -expand_arg_not_attributes = - second argument must be `attributes` - -expand_attr_no_arguments = - attribute must have either one or two arguments - -expand_attribute_meta_item = - attribute must be a meta item, not a literal - -expand_attribute_single_word = - attribute must only be a single word - expand_attributes_on_expressions_experimental = attributes on expressions are experimental .help_outer_doc = `///` is used for outer documentation comments; for a plain comment, use `//` .help_inner_doc = `//!` is used for inner documentation comments; for a plain comment, use `//` by removing the `!` or inserting a space in between them: `// !` -expand_attributes_wrong_form = - attribute must be of form: `attributes(foo, bar)` - -expand_cannot_be_name_of_macro = - `{$trait_ident}` cannot be a name of {$macro_type} macro - expand_collapse_debuginfo_illegal = illegal value for attribute #[collapse_debuginfo(no|external|yes)] @@ -71,9 +53,6 @@ expand_glob_delegation_outside_impls = expand_glob_delegation_traitless_qpath = qualified path without a trait in glob delegation -expand_helper_attribute_name_invalid = - `{$name}` cannot be a name of derive helper attribute - expand_incomplete_parse = macro expansion ignores {$descr} and any tokens following .label = caused by the macro expansion here @@ -165,12 +144,6 @@ expand_mve_unrecognized_var = expand_non_inline_modules_in_proc_macro_input_are_unstable = non-inline modules in proc macro input are unstable -expand_not_a_meta_item = - not a meta item - -expand_only_one_word = - must only be one word - expand_proc_macro_back_compat = using an old version of `{$crate_name}` .note = older versions of the `{$crate_name}` crate no longer compile; please update to `{$crate_name}` v{$fixed_version}, or switch to one of the `{$crate_name}` alternatives diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ec2ca967ca5e..afe77e766faa 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1384,80 +1384,6 @@ pub fn resolve_path(sess: &Session, path: impl Into, span: Span) -> PRe } } -pub fn parse_macro_name_and_helper_attrs( - dcx: DiagCtxtHandle<'_>, - attr: &impl AttributeExt, - macro_type: &str, -) -> Option<(Symbol, Vec)> { - // Once we've located the `#[proc_macro_derive]` attribute, verify - // that it's of the form `#[proc_macro_derive(Foo)]` or - // `#[proc_macro_derive(Foo, attributes(A, ..))]` - let list = attr.meta_item_list()?; - let ([trait_attr] | [trait_attr, _]) = list.as_slice() else { - dcx.emit_err(errors::AttrNoArguments { span: attr.span() }); - return None; - }; - let Some(trait_attr) = trait_attr.meta_item() else { - dcx.emit_err(errors::NotAMetaItem { span: trait_attr.span() }); - return None; - }; - let trait_ident = match trait_attr.ident() { - Some(trait_ident) if trait_attr.is_word() => trait_ident, - _ => { - dcx.emit_err(errors::OnlyOneWord { span: trait_attr.span }); - return None; - } - }; - - if !trait_ident.name.can_be_raw() { - dcx.emit_err(errors::CannotBeNameOfMacro { - span: trait_attr.span, - trait_ident, - macro_type, - }); - } - - let attributes_attr = list.get(1); - let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr { - if !attr.has_name(sym::attributes) { - dcx.emit_err(errors::ArgumentNotAttributes { span: attr.span() }); - } - attr.meta_item_list() - .unwrap_or_else(|| { - dcx.emit_err(errors::AttributesWrongForm { span: attr.span() }); - &[] - }) - .iter() - .filter_map(|attr| { - let Some(attr) = attr.meta_item() else { - dcx.emit_err(errors::AttributeMetaItem { span: attr.span() }); - return None; - }; - - let ident = match attr.ident() { - Some(ident) if attr.is_word() => ident, - _ => { - dcx.emit_err(errors::AttributeSingleWord { span: attr.span }); - return None; - } - }; - if !ident.name.can_be_raw() { - dcx.emit_err(errors::HelperAttributeNameInvalid { - span: attr.span, - name: ident, - }); - } - - Some(ident.name) - }) - .collect() - } else { - Vec::new() - }; - - Some((trait_ident.name, proc_attrs)) -} - /// If this item looks like a specific enums from `rental`, emit a fatal error. /// See #73345 and #83125 for more details. /// FIXME(#73933): Remove this eventually. diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index 3ac5d2130535..fd1391a554a8 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -78,72 +78,6 @@ pub(crate) struct MacroBodyStability { pub head_span: Span, } -#[derive(Diagnostic)] -#[diag(expand_attr_no_arguments)] -pub(crate) struct AttrNoArguments { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_not_a_meta_item)] -pub(crate) struct NotAMetaItem { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_only_one_word)] -pub(crate) struct OnlyOneWord { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_cannot_be_name_of_macro)] -pub(crate) struct CannotBeNameOfMacro<'a> { - #[primary_span] - pub span: Span, - pub trait_ident: Ident, - pub macro_type: &'a str, -} - -#[derive(Diagnostic)] -#[diag(expand_arg_not_attributes)] -pub(crate) struct ArgumentNotAttributes { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attributes_wrong_form)] -pub(crate) struct AttributesWrongForm { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attribute_meta_item)] -pub(crate) struct AttributeMetaItem { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_attribute_single_word)] -pub(crate) struct AttributeSingleWord { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(expand_helper_attribute_name_invalid)] -pub(crate) struct HelperAttributeNameInvalid { - #[primary_span] - pub span: Span, - pub name: Ident, -} - #[derive(Diagnostic)] #[diag(expand_feature_removed, code = E0557)] #[note] From 97b65215fffb34339fa35d8a9b18d4d008d341bd Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 7 Jul 2025 22:28:37 +0200 Subject: [PATCH 078/201] Fix tooling Signed-off-by: Jonathan Brouwer --- src/librustdoc/clean/mod.rs | 51 ++++++++----------- .../src/needless_pass_by_value.rs | 4 +- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 1265a39d27ba..14295ce0a31d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -36,6 +36,7 @@ use std::mem; use rustc_ast::token::{Token, TokenKind}; use rustc_ast::tokenstream::{TokenStream, TokenTree}; +use rustc_attr_data_structures::{AttributeKind, find_attr}; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry}; use rustc_errors::codes::*; use rustc_errors::{FatalError, struct_span_code_err}; @@ -987,28 +988,17 @@ fn clean_proc_macro<'tcx>( kind: MacroKind, cx: &mut DocContext<'tcx>, ) -> ItemKind { + if kind != MacroKind::Derive { + return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); + } let attrs = cx.tcx.hir_attrs(item.hir_id()); - if kind == MacroKind::Derive - && let Some(derive_name) = - hir_attr_lists(attrs, sym::proc_macro_derive).find_map(|mi| mi.ident()) - { - *name = derive_name.name; - } + let Some((trait_name, helper_attrs)) = find_attr!(attrs, AttributeKind::ProcMacroDerive { trait_name, helper_attrs, ..} => (*trait_name, helper_attrs)) + else { + return ProcMacroItem(ProcMacro { kind, helpers: vec![] }); + }; + *name = trait_name; + let helpers = helper_attrs.iter().copied().collect(); - let mut helpers = Vec::new(); - for mi in hir_attr_lists(attrs, sym::proc_macro_derive) { - if !mi.has_name(sym::attributes) { - continue; - } - - if let Some(list) = mi.meta_item_list() { - for inner_mi in list { - if let Some(ident) = inner_mi.ident() { - helpers.push(ident.name); - } - } - } - } ProcMacroItem(ProcMacro { kind, helpers }) } @@ -1021,17 +1011,16 @@ fn clean_fn_or_proc_macro<'tcx>( cx: &mut DocContext<'tcx>, ) -> ItemKind { let attrs = cx.tcx.hir_attrs(item.hir_id()); - let macro_kind = attrs.iter().find_map(|a| { - if a.has_name(sym::proc_macro) { - Some(MacroKind::Bang) - } else if a.has_name(sym::proc_macro_derive) { - Some(MacroKind::Derive) - } else if a.has_name(sym::proc_macro_attribute) { - Some(MacroKind::Attr) - } else { - None - } - }); + let macro_kind = if find_attr!(attrs, AttributeKind::ProcMacro(..)) { + Some(MacroKind::Bang) + } else if find_attr!(attrs, AttributeKind::ProcMacroDerive { .. }) { + Some(MacroKind::Derive) + } else if find_attr!(attrs, AttributeKind::ProcMacroAttribute(..)) { + Some(MacroKind::Attr) + } else { + None + }; + match macro_kind { Some(kind) => clean_proc_macro(item, name, kind, cx), None => { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 2006a824402d..7b057998063f 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -312,9 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { /// Functions marked with these attributes must have the exact signature. pub(crate) fn requires_exact_signature(attrs: &[Attribute]) -> bool { attrs.iter().any(|attr| { - [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive] - .iter() - .any(|&allow| attr.has_name(allow)) + attr.is_proc_macro_attr() }) } From 75420b62ffd516705dda1327a26f4f27818a189f Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 27 Jul 2025 00:27:04 +0000 Subject: [PATCH 079/201] cargo update compiler & tools dependencies: Locking 3 packages to latest compatible versions Updating ipc-channel v0.20.0 -> v0.20.1 Updating rand v0.9.1 -> v0.9.2 Updating redox_syscall v0.5.13 -> v0.5.16 note: pass `--verbose` to see 37 unchanged dependencies behind latest library dependencies: Locking 1 package to latest compatible version Updating rand v0.9.1 -> v0.9.2 note: pass `--verbose` to see 2 unchanged dependencies behind latest rustbook dependencies: Locking 1 package to latest compatible version Updating redox_syscall v0.5.13 -> v0.5.16 --- Cargo.lock | 26 +++++++++++++------------- library/Cargo.lock | 4 ++-- src/tools/rustbook/Cargo.lock | 4 ++-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e0561199ab7..d8d5211dd901 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1890,16 +1890,16 @@ dependencies = [ [[package]] name = "ipc-channel" -version = "0.20.0" +version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1c98b70019c830a1fc39cecfe1f60ff99c4122f0a189697c810c90ec545c14" +checksum = "1700f6b8b9f00cdd675f32fbb3a5be882213140dfe045805273221ca266c43f8" dependencies = [ "bincode", "crossbeam-channel", "fnv", "libc", "mio", - "rand 0.9.1", + "rand 0.9.2", "serde", "tempfile", "uuid", @@ -2329,7 +2329,7 @@ dependencies = [ "libloading", "measureme", "nix", - "rand 0.9.1", + "rand 0.9.2", "regex", "rustc_version", "serde", @@ -2962,9 +2962,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -3048,9 +3048,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "7251471db004e509f4e75a62cca9435365b5ec7bcdff530d612ac7c87c44a792" dependencies = [ "bitflags", ] @@ -3245,7 +3245,7 @@ name = "rustc_abi" version = "0.0.0" dependencies = [ "bitflags", - "rand 0.9.1", + "rand 0.9.2", "rand_xoshiro", "rustc_data_structures", "rustc_hashes", @@ -3880,7 +3880,7 @@ dependencies = [ name = "rustc_incremental" version = "0.0.0" dependencies = [ - "rand 0.9.1", + "rand 0.9.2", "rustc_ast", "rustc_data_structures", "rustc_errors", @@ -4493,7 +4493,7 @@ dependencies = [ "bitflags", "getopts", "libc", - "rand 0.9.1", + "rand 0.9.2", "rustc_abi", "rustc_ast", "rustc_data_structures", @@ -4578,7 +4578,7 @@ dependencies = [ "crossbeam-deque", "crossbeam-utils", "libc", - "rand 0.9.1", + "rand 0.9.2", "rand_xorshift", "scoped-tls", "smallvec", @@ -5284,7 +5284,7 @@ version = "0.1.0" dependencies = [ "indicatif", "num", - "rand 0.9.1", + "rand 0.9.2", "rand_chacha 0.9.0", "rayon", ] diff --git a/library/Cargo.lock b/library/Cargo.lock index 94720397fdfe..a9a611fe1ed5 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -237,9 +237,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_core", ] diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index e363668d4620..5f30c75732c7 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -1343,9 +1343,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "7251471db004e509f4e75a62cca9435365b5ec7bcdff530d612ac7c87c44a792" dependencies = [ "bitflags 2.9.1", ] From 3b0988b6e9f92d9aadff2943e7a4004babcfdcb7 Mon Sep 17 00:00:00 2001 From: Young-Flash Date: Sun, 27 Jul 2025 10:54:43 +0800 Subject: [PATCH 080/201] minor: fix typo --- src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index b06b3f82e36c..1901bcc797e7 100755 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -700,7 +700,7 @@ type Foo = foo< fn foo( very_long_parameter_name: u32, another_very_long_parameter_name: u32, - third_very_long_parm: u32, + third_very_long_param: u32, ) { todo!() } From 558796b4600828d5fa731e8a2c9364a282a995d5 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sun, 27 Jul 2025 01:37:03 +0200 Subject: [PATCH 081/201] rustdoc: save target modifiers `rustdoc` was filling a `target_modifiers` variable, but it was not using the result. In turn, that means that trying to use a dependency that set a target modifier fails. For instance, running: ```sh RUSTC_BOOTSTRAP=1 rustc --edition=2024 --target=aarch64-unknown-none-softfloat --sysroot=/dev/null --emit=metadata -Zfixed-x18 --crate-type rlib --crate-name core $(rustc --print sysroot)/lib/rustlib/src/rust/library/core/src/lib.rs echo '#![allow(internal_features)] ' | RUSTC_BOOTSTRAP=1 rustdoc --edition=2021 --target=aarch64-unknown-none-softfloat --sysroot=/dev/null -Zfixed-x18 --extern core=libcore.rmeta - ``` will fail with: ```text error: mixing `-Zfixed-x18` will cause an ABI mismatch in crate `rust_out` | = help: the `-Zfixed-x18` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely = note: unset `-Zfixed-x18` in this crate is incompatible with `-Zfixed-x18=` in dependency `core` = help: set `-Zfixed-x18=` in this crate or unset `-Zfixed-x18` in `core` = help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=fixed-x18` to silence this error ``` Thus save the targets modifiers in `Options` to then pass it to the session options, so that eventually the diff can be performed as expected in `report_incompatible_target_modifiers()`. Fixes: https://github.com/rust-lang/rust/issues/144521 Signed-off-by: Miguel Ojeda --- src/librustdoc/config.rs | 4 +++ src/librustdoc/core.rs | 2 ++ tests/run-make/rustdoc-target-modifiers/c.rs | 7 +++++ tests/run-make/rustdoc-target-modifiers/d.rs | 12 ++++++++ .../rustdoc-target-modifiers/rmake.rs | 28 +++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 tests/run-make/rustdoc-target-modifiers/c.rs create mode 100644 tests/run-make/rustdoc-target-modifiers/d.rs create mode 100644 tests/run-make/rustdoc-target-modifiers/rmake.rs diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 986390dbaa08..57456e906de5 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -173,6 +173,9 @@ pub(crate) struct Options { /// Arguments to be used when compiling doctests. pub(crate) doctest_build_args: Vec, + + /// Target modifiers. + pub(crate) target_modifiers: BTreeMap, } impl fmt::Debug for Options { @@ -846,6 +849,7 @@ impl Options { unstable_features, expanded_args: args, doctest_build_args, + target_modifiers, }; let render_options = RenderOptions { output, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index bd57bb21e639..e89733b2f6d5 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -214,6 +214,7 @@ pub(crate) fn create_config( scrape_examples_options, expanded_args, remap_path_prefix, + target_modifiers, .. }: RustdocOptions, render_options: &RenderOptions, @@ -277,6 +278,7 @@ pub(crate) fn create_config( } else { OutputTypes::new(&[]) }, + target_modifiers, ..Options::default() }; diff --git a/tests/run-make/rustdoc-target-modifiers/c.rs b/tests/run-make/rustdoc-target-modifiers/c.rs new file mode 100644 index 000000000000..287d0bbd7252 --- /dev/null +++ b/tests/run-make/rustdoc-target-modifiers/c.rs @@ -0,0 +1,7 @@ +#![allow(internal_features)] +#![feature(lang_items, no_core)] +#![no_core] + +fn f() { + d::f(); +} diff --git a/tests/run-make/rustdoc-target-modifiers/d.rs b/tests/run-make/rustdoc-target-modifiers/d.rs new file mode 100644 index 000000000000..6cbff06079ed --- /dev/null +++ b/tests/run-make/rustdoc-target-modifiers/d.rs @@ -0,0 +1,12 @@ +#![allow(internal_features)] +#![feature(lang_items, no_core)] +#![no_core] + +#[lang = "pointee_sized"] +pub trait PointeeSized {} +#[lang = "meta_sized"] +pub trait MetaSized: PointeeSized {} +#[lang = "sized"] +pub trait Sized: MetaSized {} + +pub fn f() {} diff --git a/tests/run-make/rustdoc-target-modifiers/rmake.rs b/tests/run-make/rustdoc-target-modifiers/rmake.rs new file mode 100644 index 000000000000..ee522501fd28 --- /dev/null +++ b/tests/run-make/rustdoc-target-modifiers/rmake.rs @@ -0,0 +1,28 @@ +//! Test that target modifiers are taken into account by `rustdoc`. +//! +//! Otherwise, `rustdoc` errors when trying to generate documentation +//! using dependencies (e.g. `core`) that set a target modifier. +//! +//! Please see https://github.com/rust-lang/rust/issues/144521. + +use run_make_support::{rustc, rustdoc}; + +fn main() { + rustc() + .input("d.rs") + .edition("2024") + .crate_type("rlib") + .emit("metadata") + .sysroot("/dev/null") + .target("aarch64-unknown-none-softfloat") + .arg("-Zfixed-x18") + .run(); + + rustdoc() + .input("c.rs") + .crate_type("rlib") + .extern_("d", "libd.rmeta") + .target("aarch64-unknown-none-softfloat") + .arg("-Zfixed-x18") + .run(); +} From 3d3534d179ecd1d1a8bbaa4aad5488db67c4242d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 27 Jul 2025 12:13:45 +0530 Subject: [PATCH 082/201] add download context --- src/bootstrap/src/core/download.rs | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 373fcd52052e..19adfab3f367 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -852,6 +852,43 @@ download-rustc = false } } +/// Only should be used for pre config initialization downloads. +pub(crate) struct DownloadContext<'a> { + host_target: TargetSelection, + out: &'a Path, + patch_binaries_for_nix: Option, + exec_ctx: &'a ExecutionContext, + verbose: bool, + stage0_metadata: &'a build_helper::stage0_parser::Stage0, + llvm_assertions: bool, + bootstrap_cache_path: &'a Option, + is_running_on_ci: bool, + dry_run: bool, +} + +impl<'a> AsRef> for DownloadContext<'a> { + fn as_ref(&self) -> &DownloadContext<'a> { + self + } +} + +impl<'a> From<&'a Config> for DownloadContext<'a> { + fn from(value: &'a Config) -> Self { + DownloadContext { + host_target: value.host_target, + out: &value.out, + patch_binaries_for_nix: value.patch_binaries_for_nix, + exec_ctx: &value.exec_ctx, + verbose: value.verbose > 0, + stage0_metadata: &value.stage0_metadata, + llvm_assertions: value.llvm_assertions, + bootstrap_cache_path: &value.bootstrap_cache_path, + is_running_on_ci: value.is_running_on_ci, + dry_run: value.dry_run(), + } + } +} + fn path_is_dylib(path: &Path) -> bool { // The .so is not necessarily the extension, it might be libLLVM.so.18.1 path.to_str().is_some_and(|path| path.contains(".so")) From 861de92f947d9431f641afc2d589b366521f091d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 27 Jul 2025 12:15:08 +0530 Subject: [PATCH 083/201] move utility methods out of config impl --- src/bootstrap/src/core/download.rs | 979 +++++++++++++++-------------- 1 file changed, 518 insertions(+), 461 deletions(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 19adfab3f367..f3a10048b655 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -7,9 +7,9 @@ use std::sync::OnceLock; use xz2::bufread::XzDecoder; -use crate::core::config::BUILDER_CONFIG_FILENAME; +use crate::core::config::{BUILDER_CONFIG_FILENAME, TargetSelection}; use crate::utils::build_stamp::BuildStamp; -use crate::utils::exec::command; +use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, hex_encode, move_file}; use crate::{Config, t}; @@ -24,17 +24,6 @@ fn extract_curl_version(out: String) -> semver::Version { .unwrap_or(semver::Version::new(1, 0, 0)) } -fn curl_version(config: &Config) -> semver::Version { - let mut curl = command("curl"); - curl.arg("-V"); - let curl = curl.run_capture_stdout(config); - if curl.is_failure() { - return semver::Version::new(1, 0, 0); - } - let output = curl.stdout(); - extract_curl_version(output) -} - /// Generic helpers that are useful anywhere in bootstrap. impl Config { pub fn is_verbose(&self) -> bool { @@ -49,10 +38,7 @@ impl Config { } pub(crate) fn remove(&self, f: &Path) { - if self.dry_run() { - return; - } - fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); + remove(self.dry_run(), f); } /// Create a temporary directory in `out` and return its path. @@ -68,49 +54,7 @@ impl Config { /// Whether or not `fix_bin_or_dylib` needs to be run; can only be true /// on NixOS fn should_fix_bins_and_dylibs(&self) -> bool { - let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| { - let uname = command("uname").allow_failure().arg("-s").run_capture_stdout(self); - if uname.is_failure() { - return false; - } - let output = uname.stdout(); - if !output.starts_with("Linux") { - return false; - } - // If the user has asked binaries to be patched for Nix, then - // don't check for NixOS or `/lib`. - // NOTE: this intentionally comes after the Linux check: - // - patchelf only works with ELF files, so no need to run it on Mac or Windows - // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. - if let Some(explicit_value) = self.patch_binaries_for_nix { - return explicit_value; - } - - // Use `/etc/os-release` instead of `/etc/NIXOS`. - // The latter one does not exist on NixOS when using tmpfs as root. - let is_nixos = match File::open("/etc/os-release") { - Err(e) if e.kind() == ErrorKind::NotFound => false, - Err(e) => panic!("failed to access /etc/os-release: {e}"), - Ok(os_release) => BufReader::new(os_release).lines().any(|l| { - let l = l.expect("reading /etc/os-release"); - matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") - }), - }; - if !is_nixos { - let in_nix_shell = env::var("IN_NIX_SHELL"); - if let Ok(in_nix_shell) = in_nix_shell { - eprintln!( - "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \ - you may need to set `patch-binaries-for-nix=true` in bootstrap.toml" - ); - } - } - is_nixos - }); - if val { - eprintln!("INFO: You seem to be using Nix."); - } - val + should_fix_bins_and_dylibs(self.patch_binaries_for_nix, &self.exec_ctx) } /// Modifies the interpreter section of 'fname' to fix the dynamic linker, @@ -121,259 +65,22 @@ impl Config { /// /// Please see for more information fn fix_bin_or_dylib(&self, fname: &Path) { - assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true)); - println!("attempting to patch {}", fname.display()); - - // Only build `.nix-deps` once. - static NIX_DEPS_DIR: OnceLock = OnceLock::new(); - let mut nix_build_succeeded = true; - let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { - // Run `nix-build` to "build" each dependency (which will likely reuse - // the existing `/nix/store` copy, or at most download a pre-built copy). - // - // Importantly, we create a gc-root called `.nix-deps` in the `build/` - // directory, but still reference the actual `/nix/store` path in the rpath - // as it makes it significantly more robust against changes to the location of - // the `.nix-deps` location. - // - // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). - // zlib: Needed as a system dependency of `libLLVM-*.so`. - // patchelf: Needed for patching ELF binaries (see doc comment above). - let nix_deps_dir = self.out.join(".nix-deps"); - const NIX_EXPR: &str = " - with (import {}); - symlinkJoin { - name = \"rust-stage0-dependencies\"; - paths = [ - zlib - patchelf - stdenv.cc.bintools - ]; - } - "; - nix_build_succeeded = command("nix-build") - .allow_failure() - .args([Path::new("-E"), Path::new(NIX_EXPR), Path::new("-o"), &nix_deps_dir]) - .run_capture_stdout(self) - .is_success(); - nix_deps_dir - }); - if !nix_build_succeeded { - return; - } - - let mut patchelf = command(nix_deps_dir.join("bin/patchelf")); - patchelf.args(&[ - OsString::from("--add-rpath"), - OsString::from(t!(fs::canonicalize(nix_deps_dir)).join("lib")), - ]); - if !path_is_dylib(fname) { - // Finally, set the correct .interp for binaries - let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); - let dynamic_linker = t!(fs::read_to_string(dynamic_linker_path)); - patchelf.args(["--set-interpreter", dynamic_linker.trim_end()]); - } - patchelf.arg(fname); - let _ = patchelf.allow_failure().run_capture_stdout(self); + fix_bin_or_dylib(&self.out, fname, &self.exec_ctx); } fn download_file(&self, url: &str, dest_path: &Path, help_on_error: &str) { - self.verbose(|| println!("download {url}")); - // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. - let tempfile = self.tempdir().join(dest_path.file_name().unwrap()); - // While bootstrap itself only supports http and https downloads, downstream forks might - // need to download components from other protocols. The match allows them adding more - // protocols without worrying about merge conflicts if we change the HTTP implementation. - match url.split_once("://").map(|(proto, _)| proto) { - Some("http") | Some("https") => { - self.download_http_with_retries(&tempfile, url, help_on_error) - } - Some(other) => panic!("unsupported protocol {other} in {url}"), - None => panic!("no protocol in {url}"), - } - t!( - move_file(&tempfile, dest_path), - format!("failed to rename {tempfile:?} to {dest_path:?}") - ); - } - - fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) { - println!("downloading {url}"); - // Try curl. If that fails and we are on windows, fallback to PowerShell. - // options should be kept in sync with - // src/bootstrap/src/core/download.rs - // for consistency - let mut curl = command("curl").allow_failure(); - curl.args([ - // follow redirect - "--location", - // timeout if speed is < 10 bytes/sec for > 30 seconds - "--speed-time", - "30", - "--speed-limit", - "10", - // timeout if cannot connect within 30 seconds - "--connect-timeout", - "30", - // output file - "--output", - tempfile.to_str().unwrap(), - // if there is an error, don't restart the download, - // instead continue where it left off. - "--continue-at", - "-", - // retry up to 3 times. note that this means a maximum of 4 - // attempts will be made, since the first attempt isn't a *re*try. - "--retry", - "3", - // show errors, even if --silent is specified - "--show-error", - // set timestamp of downloaded file to that of the server - "--remote-time", - // fail on non-ok http status - "--fail", - ]); - // Don't print progress in CI; the \r wrapping looks bad and downloads don't take long enough for progress to be useful. - if self.is_running_on_ci { - curl.arg("--silent"); - } else { - curl.arg("--progress-bar"); - } - // --retry-all-errors was added in 7.71.0, don't use it if curl is old. - if curl_version(self) >= semver::Version::new(7, 71, 0) { - curl.arg("--retry-all-errors"); - } - curl.arg(url); - if !curl.run(self) { - if self.host_target.contains("windows-msvc") { - eprintln!("Fallback to PowerShell"); - for _ in 0..3 { - let powershell = command("PowerShell.exe").allow_failure().args([ - "/nologo", - "-Command", - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", - &format!( - "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", - url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), - ), - ]).run_capture_stdout(self); - - if powershell.is_failure() { - return; - } - - eprintln!("\nspurious failure, trying again"); - } - } - if !help_on_error.is_empty() { - eprintln!("{help_on_error}"); - } - crate::exit!(1); - } + let dwn_ctx: DownloadContext<'_> = self.into(); + download_file(dwn_ctx, url, dest_path, help_on_error); } fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) { - eprintln!("extracting {} to {}", tarball.display(), dst.display()); - if !dst.exists() { - t!(fs::create_dir_all(dst)); - } - - // `tarball` ends with `.tar.xz`; strip that suffix - // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` - let uncompressed_filename = - Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); - let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); - - // decompress the file - let data = t!(File::open(tarball), format!("file {} not found", tarball.display())); - let decompressor = XzDecoder::new(BufReader::new(data)); - - let mut tar = tar::Archive::new(decompressor); - - let is_ci_rustc = dst.ends_with("ci-rustc"); - let is_ci_llvm = dst.ends_with("ci-llvm"); - - // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding - // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. - // Cache the entries when we extract it so we only have to read it once. - let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None }; - - for member in t!(tar.entries()) { - let mut member = t!(member); - let original_path = t!(member.path()).into_owned(); - // skip the top-level directory - if original_path == directory_prefix { - continue; - } - let mut short_path = t!(original_path.strip_prefix(directory_prefix)); - let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME); - - if !(short_path.starts_with(pattern) - || ((is_ci_rustc || is_ci_llvm) && is_builder_config)) - { - continue; - } - short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); - let dst_path = dst.join(short_path); - self.verbose(|| { - println!("extracting {} to {}", original_path.display(), dst.display()) - }); - if !t!(member.unpack_in(dst)) { - panic!("path traversal attack ??"); - } - if let Some(record) = &mut recorded_entries { - t!(writeln!(record, "{}", short_path.to_str().unwrap())); - } - let src_path = dst.join(original_path); - if src_path.is_dir() && dst_path.exists() { - continue; - } - t!(move_file(src_path, dst_path)); - } - let dst_dir = dst.join(directory_prefix); - if dst_dir.exists() { - t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display())); - } + unpack(self.verbose > 0, tarball, dst, pattern); } /// Returns whether the SHA256 checksum of `path` matches `expected`. + #[cfg(test)] pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool { - use sha2::Digest; - - self.verbose(|| println!("verifying {}", path.display())); - - if self.dry_run() { - return false; - } - - let mut hasher = sha2::Sha256::new(); - - let file = t!(File::open(path)); - let mut reader = BufReader::new(file); - - loop { - let buffer = t!(reader.fill_buf()); - let l = buffer.len(); - // break if EOF - if l == 0 { - break; - } - hasher.update(buffer); - reader.consume(l); - } - - let checksum = hex_encode(hasher.finalize().as_slice()); - let verified = checksum == expected; - - if !verified { - println!( - "invalid checksum: \n\ - found: {checksum}\n\ - expected: {expected}", - ); - } - - verified + verify(self.verbose > 0, self.dry_run(), path, expected) } } @@ -388,6 +95,7 @@ fn recorded_entries(dst: &Path, pattern: &str) -> Option> { Some(BufWriter::new(t!(File::create(dst.join(name))))) } +#[derive(Clone)] enum DownloadSource { CI, Dist, @@ -420,61 +128,11 @@ impl Config { cargo_clippy } - #[cfg(test)] - pub(crate) fn maybe_download_rustfmt(&self) -> Option { - Some(PathBuf::new()) - } - /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't /// reuse target directories or artifacts - #[cfg(not(test))] pub(crate) fn maybe_download_rustfmt(&self) -> Option { - use build_helper::stage0_parser::VersionMetadata; - - if self.dry_run() { - return Some(PathBuf::new()); - } - - let VersionMetadata { date, version } = self.stage0_metadata.rustfmt.as_ref()?; - let channel = format!("{version}-{date}"); - - let host = self.host_target; - let bin_root = self.out.join(host).join("rustfmt"); - let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); - let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); - if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { - return Some(rustfmt_path); - } - - self.download_component( - DownloadSource::Dist, - format!("rustfmt-{version}-{build}.tar.xz", build = host.triple), - "rustfmt-preview", - date, - "rustfmt", - ); - self.download_component( - DownloadSource::Dist, - format!("rustc-{version}-{build}.tar.xz", build = host.triple), - "rustc", - date, - "rustfmt", - ); - - if self.should_fix_bins_and_dylibs() { - self.fix_bin_or_dylib(&bin_root.join("bin").join("rustfmt")); - self.fix_bin_or_dylib(&bin_root.join("bin").join("cargo-fmt")); - let lib_dir = bin_root.join("lib"); - for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { - let lib = t!(lib); - if path_is_dylib(&lib.path()) { - self.fix_bin_or_dylib(&lib.path()); - } - } - } - - t!(rustfmt_stamp.write()); - Some(rustfmt_path) + let dwn_ctx: DownloadContext<'_> = self.into(); + maybe_download_rustfmt(dwn_ctx) } pub(crate) fn ci_rust_std_contents(&self) -> Vec { @@ -514,28 +172,9 @@ impl Config { ); } - #[cfg(test)] - pub(crate) fn download_beta_toolchain(&self) {} - - #[cfg(not(test))] pub(crate) fn download_beta_toolchain(&self) { - self.verbose(|| println!("downloading stage0 beta artifacts")); - - let date = &self.stage0_metadata.compiler.date; - let version = &self.stage0_metadata.compiler.version; - let extra_components = ["cargo"]; - - let download_beta_component = |config: &Config, filename, prefix: &_, date: &_| { - config.download_component(DownloadSource::Dist, filename, prefix, date, "stage0") - }; - - self.download_toolchain( - version, - "stage0", - date, - &extra_components, - download_beta_component, - ); + let dwn_ctx: DownloadContext<'_> = self.into(); + download_beta_toolchain(dwn_ctx); } fn download_toolchain( @@ -607,91 +246,8 @@ impl Config { key: &str, destination: &str, ) { - if self.dry_run() { - return; - } - - let cache_dst = - self.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| self.out.join("cache")); - - let cache_dir = cache_dst.join(key); - if !cache_dir.exists() { - t!(fs::create_dir_all(&cache_dir)); - } - - let bin_root = self.out.join(self.host_target).join(destination); - let tarball = cache_dir.join(&filename); - let (base_url, url, should_verify) = match mode { - DownloadSource::CI => { - let dist_server = if self.llvm_assertions { - self.stage0_metadata.config.artifacts_with_llvm_assertions_server.clone() - } else { - self.stage0_metadata.config.artifacts_server.clone() - }; - let url = format!( - "{}/{filename}", - key.strip_suffix(&format!("-{}", self.llvm_assertions)).unwrap() - ); - (dist_server, url, false) - } - DownloadSource::Dist => { - let dist_server = env::var("RUSTUP_DIST_SERVER") - .unwrap_or(self.stage0_metadata.config.dist_server.to_string()); - // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0 - (dist_server, format!("dist/{key}/{filename}"), true) - } - }; - - // For the stage0 compiler, put special effort into ensuring the checksums are valid. - let checksum = if should_verify { - let error = format!( - "src/stage0 doesn't contain a checksum for {url}. \ - Pre-built artifacts might not be available for this \ - target at this time, see https://doc.rust-lang.org/nightly\ - /rustc/platform-support.html for more information." - ); - let sha256 = self.stage0_metadata.checksums_sha256.get(&url).expect(&error); - if tarball.exists() { - if self.verify(&tarball, sha256) { - self.unpack(&tarball, &bin_root, prefix); - return; - } else { - self.verbose(|| { - println!( - "ignoring cached file {} due to failed verification", - tarball.display() - ) - }); - self.remove(&tarball); - } - } - Some(sha256) - } else if tarball.exists() { - self.unpack(&tarball, &bin_root, prefix); - return; - } else { - None - }; - - let mut help_on_error = ""; - if destination == "ci-rustc" { - help_on_error = "ERROR: failed to download pre-built rustc from CI - -NOTE: old builds get deleted after a certain time -HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml: - -[rust] -download-rustc = false -"; - } - self.download_file(&format!("{base_url}/{url}"), &tarball, help_on_error); - if let Some(sha256) = checksum - && !self.verify(&tarball, sha256) - { - panic!("failed to verify {}", tarball.display()); - } - - self.unpack(&tarball, &bin_root, prefix); + let dwn_ctx: DownloadContext<'_> = self.into(); + download_component(dwn_ctx, mode, filename, prefix, key, destination); } #[cfg(test)] @@ -934,3 +490,504 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo SUPPORTED_PLATFORMS.contains(&target_triple) } } + +fn download_toolchain<'a>( + dwn_ctx: impl AsRef>, + version: &str, + sysroot: &str, + stamp_key: &str, + extra_components: &[&str], + destination: &str, + mode: DownloadSource, +) { + let dwn_ctx = dwn_ctx.as_ref(); + let host = dwn_ctx.host_target.triple; + let bin_root = dwn_ctx.out.join(host).join(sysroot); + let rustc_stamp = BuildStamp::new(&bin_root).with_prefix("rustc").add_stamp(stamp_key); + + if !bin_root.join("bin").join(exe("rustc", dwn_ctx.host_target)).exists() + || !rustc_stamp.is_up_to_date() + { + if bin_root.exists() { + t!(fs::remove_dir_all(&bin_root)); + } + let filename = format!("rust-std-{version}-{host}.tar.xz"); + let pattern = format!("rust-std-{host}"); + download_component(dwn_ctx, mode.clone(), filename, &pattern, stamp_key, destination); + let filename = format!("rustc-{version}-{host}.tar.xz"); + download_component(dwn_ctx, mode.clone(), filename, "rustc", stamp_key, destination); + + for component in extra_components { + let filename = format!("{component}-{version}-{host}.tar.xz"); + download_component(dwn_ctx, mode.clone(), filename, component, stamp_key, destination); + } + + if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) { + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustc"), dwn_ctx.exec_ctx); + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustdoc"), dwn_ctx.exec_ctx); + fix_bin_or_dylib( + dwn_ctx.out, + &bin_root.join("libexec").join("rust-analyzer-proc-macro-srv"), + dwn_ctx.exec_ctx, + ); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if path_is_dylib(&lib.path()) { + fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx); + } + } + } + + t!(rustc_stamp.write()); + } +} + +pub(crate) fn remove(dry_run: bool, f: &Path) { + if dry_run { + return; + } + fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); +} + +fn fix_bin_or_dylib(out: &Path, fname: &Path, exec_ctx: &ExecutionContext) { + assert_eq!(SHOULD_FIX_BINS_AND_DYLIBS.get(), Some(&true)); + println!("attempting to patch {}", fname.display()); + + // Only build `.nix-deps` once. + static NIX_DEPS_DIR: OnceLock = OnceLock::new(); + let mut nix_build_succeeded = true; + let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| { + // Run `nix-build` to "build" each dependency (which will likely reuse + // the existing `/nix/store` copy, or at most download a pre-built copy). + // + // Importantly, we create a gc-root called `.nix-deps` in the `build/` + // directory, but still reference the actual `/nix/store` path in the rpath + // as it makes it significantly more robust against changes to the location of + // the `.nix-deps` location. + // + // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`). + // zlib: Needed as a system dependency of `libLLVM-*.so`. + // patchelf: Needed for patching ELF binaries (see doc comment above). + let nix_deps_dir = out.join(".nix-deps"); + const NIX_EXPR: &str = " + with (import {}); + symlinkJoin { + name = \"rust-stage0-dependencies\"; + paths = [ + zlib + patchelf + stdenv.cc.bintools + ]; + } + "; + nix_build_succeeded = command("nix-build") + .allow_failure() + .args([Path::new("-E"), Path::new(NIX_EXPR), Path::new("-o"), &nix_deps_dir]) + .run_capture_stdout(exec_ctx) + .is_success(); + nix_deps_dir + }); + if !nix_build_succeeded { + return; + } + + let mut patchelf = command(nix_deps_dir.join("bin/patchelf")); + patchelf.args(&[ + OsString::from("--add-rpath"), + OsString::from(t!(fs::canonicalize(nix_deps_dir)).join("lib")), + ]); + if !path_is_dylib(fname) { + // Finally, set the correct .interp for binaries + let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker"); + let dynamic_linker = t!(fs::read_to_string(dynamic_linker_path)); + patchelf.args(["--set-interpreter", dynamic_linker.trim_end()]); + } + patchelf.arg(fname); + let _ = patchelf.allow_failure().run_capture_stdout(exec_ctx); +} + +fn should_fix_bins_and_dylibs( + patch_binaries_for_nix: Option, + exec_ctx: &ExecutionContext, +) -> bool { + let val = *SHOULD_FIX_BINS_AND_DYLIBS.get_or_init(|| { + let uname = command("uname").allow_failure().arg("-s").run_capture_stdout(exec_ctx); + if uname.is_failure() { + return false; + } + let output = uname.stdout(); + if !output.starts_with("Linux") { + return false; + } + // If the user has asked binaries to be patched for Nix, then + // don't check for NixOS or `/lib`. + // NOTE: this intentionally comes after the Linux check: + // - patchelf only works with ELF files, so no need to run it on Mac or Windows + // - On other Unix systems, there is no stable syscall interface, so Nix doesn't manage the global libc. + if let Some(explicit_value) = patch_binaries_for_nix { + return explicit_value; + } + + // Use `/etc/os-release` instead of `/etc/NIXOS`. + // The latter one does not exist on NixOS when using tmpfs as root. + let is_nixos = match File::open("/etc/os-release") { + Err(e) if e.kind() == ErrorKind::NotFound => false, + Err(e) => panic!("failed to access /etc/os-release: {e}"), + Ok(os_release) => BufReader::new(os_release).lines().any(|l| { + let l = l.expect("reading /etc/os-release"); + matches!(l.trim(), "ID=nixos" | "ID='nixos'" | "ID=\"nixos\"") + }), + }; + if !is_nixos { + let in_nix_shell = env::var("IN_NIX_SHELL"); + if let Ok(in_nix_shell) = in_nix_shell { + eprintln!( + "The IN_NIX_SHELL environment variable is `{in_nix_shell}`; \ + you may need to set `patch-binaries-for-nix=true` in bootstrap.toml" + ); + } + } + is_nixos + }); + if val { + eprintln!("INFO: You seem to be using Nix."); + } + val +} + +fn download_component<'a>( + dwn_ctx: impl AsRef>, + mode: DownloadSource, + filename: String, + prefix: &str, + key: &str, + destination: &str, +) { + let dwn_ctx = dwn_ctx.as_ref(); + + if dwn_ctx.dry_run { + return; + } + + let cache_dst = + dwn_ctx.bootstrap_cache_path.as_ref().cloned().unwrap_or_else(|| dwn_ctx.out.join("cache")); + + let cache_dir = cache_dst.join(key); + if !cache_dir.exists() { + t!(fs::create_dir_all(&cache_dir)); + } + + let bin_root = dwn_ctx.out.join(dwn_ctx.host_target).join(destination); + let tarball = cache_dir.join(&filename); + let (base_url, url, should_verify) = match mode { + DownloadSource::CI => { + let dist_server = if dwn_ctx.llvm_assertions { + dwn_ctx.stage0_metadata.config.artifacts_with_llvm_assertions_server.clone() + } else { + dwn_ctx.stage0_metadata.config.artifacts_server.clone() + }; + let url = format!( + "{}/{filename}", + key.strip_suffix(&format!("-{}", dwn_ctx.llvm_assertions)).unwrap() + ); + (dist_server, url, false) + } + DownloadSource::Dist => { + let dist_server = env::var("RUSTUP_DIST_SERVER") + .unwrap_or(dwn_ctx.stage0_metadata.config.dist_server.to_string()); + // NOTE: make `dist` part of the URL because that's how it's stored in src/stage0 + (dist_server, format!("dist/{key}/{filename}"), true) + } + }; + + // For the stage0 compiler, put special effort into ensuring the checksums are valid. + let checksum = if should_verify { + let error = format!( + "src/stage0 doesn't contain a checksum for {url}. \ + Pre-built artifacts might not be available for this \ + target at this time, see https://doc.rust-lang.org/nightly\ + /rustc/platform-support.html for more information." + ); + let sha256 = dwn_ctx.stage0_metadata.checksums_sha256.get(&url).expect(&error); + if tarball.exists() { + if verify(dwn_ctx.verbose, dwn_ctx.dry_run, &tarball, sha256) { + unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); + return; + } else { + if dwn_ctx.verbose { + println!( + "ignoring cached file {} due to failed verification", + tarball.display() + ) + } + remove(dwn_ctx.dry_run, &tarball); + } + } + Some(sha256) + } else if tarball.exists() { + unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); + return; + } else { + None + }; + + let mut help_on_error = ""; + if destination == "ci-rustc" { + help_on_error = "ERROR: failed to download pre-built rustc from CI + +NOTE: old builds get deleted after a certain time +HELP: if trying to compile an old commit of rustc, disable `download-rustc` in bootstrap.toml: + +[rust] +download-rustc = false +"; + } + download_file(dwn_ctx, &format!("{base_url}/{url}"), &tarball, help_on_error); + if let Some(sha256) = checksum + && !verify(dwn_ctx.verbose, dwn_ctx.dry_run, &tarball, sha256) + { + panic!("failed to verify {}", tarball.display()); + } + + unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); +} + +pub(crate) fn verify(verbose: bool, dry_run: bool, path: &Path, expected: &str) -> bool { + use sha2::Digest; + + if verbose { + println!("verifying {}", path.display()); + } + + if dry_run { + return false; + } + + let mut hasher = sha2::Sha256::new(); + + let file = t!(File::open(path)); + let mut reader = BufReader::new(file); + + loop { + let buffer = t!(reader.fill_buf()); + let l = buffer.len(); + // break if EOF + if l == 0 { + break; + } + hasher.update(buffer); + reader.consume(l); + } + + let checksum = hex_encode(hasher.finalize().as_slice()); + let verified = checksum == expected; + + if !verified { + println!( + "invalid checksum: \n\ + found: {checksum}\n\ + expected: {expected}", + ); + } + + verified +} + +fn unpack(verbose: bool, tarball: &Path, dst: &Path, pattern: &str) { + eprintln!("extracting {} to {}", tarball.display(), dst.display()); + if !dst.exists() { + t!(fs::create_dir_all(dst)); + } + + // `tarball` ends with `.tar.xz`; strip that suffix + // example: `rust-dev-nightly-x86_64-unknown-linux-gnu` + let uncompressed_filename = + Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap(); + let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap()); + + // decompress the file + let data = t!(File::open(tarball), format!("file {} not found", tarball.display())); + let decompressor = XzDecoder::new(BufReader::new(data)); + + let mut tar = tar::Archive::new(decompressor); + + let is_ci_rustc = dst.ends_with("ci-rustc"); + let is_ci_llvm = dst.ends_with("ci-llvm"); + + // `compile::Sysroot` needs to know the contents of the `rustc-dev` tarball to avoid adding + // it to the sysroot unless it was explicitly requested. But parsing the 100 MB tarball is slow. + // Cache the entries when we extract it so we only have to read it once. + let mut recorded_entries = if is_ci_rustc { recorded_entries(dst, pattern) } else { None }; + + for member in t!(tar.entries()) { + let mut member = t!(member); + let original_path = t!(member.path()).into_owned(); + // skip the top-level directory + if original_path == directory_prefix { + continue; + } + let mut short_path = t!(original_path.strip_prefix(directory_prefix)); + let is_builder_config = short_path.to_str() == Some(BUILDER_CONFIG_FILENAME); + + if !(short_path.starts_with(pattern) || ((is_ci_rustc || is_ci_llvm) && is_builder_config)) + { + continue; + } + short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); + let dst_path = dst.join(short_path); + if verbose { + println!("extracting {} to {}", original_path.display(), dst.display()); + } + + if !t!(member.unpack_in(dst)) { + panic!("path traversal attack ??"); + } + if let Some(record) = &mut recorded_entries { + t!(writeln!(record, "{}", short_path.to_str().unwrap())); + } + let src_path = dst.join(original_path); + if src_path.is_dir() && dst_path.exists() { + continue; + } + t!(move_file(src_path, dst_path)); + } + let dst_dir = dst.join(directory_prefix); + if dst_dir.exists() { + t!(fs::remove_dir_all(&dst_dir), format!("failed to remove {}", dst_dir.display())); + } +} + +fn download_file<'a>( + dwn_ctx: impl AsRef>, + url: &str, + dest_path: &Path, + help_on_error: &str, +) { + let dwn_ctx = dwn_ctx.as_ref(); + + if dwn_ctx.verbose { + println!("download {url}"); + } + // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. + let tempfile = tempdir(dwn_ctx.out).join(dest_path.file_name().unwrap()); + // While bootstrap itself only supports http and https downloads, downstream forks might + // need to download components from other protocols. The match allows them adding more + // protocols without worrying about merge conflicts if we change the HTTP implementation. + match url.split_once("://").map(|(proto, _)| proto) { + Some("http") | Some("https") => download_http_with_retries( + dwn_ctx.host_target, + dwn_ctx.is_running_on_ci, + dwn_ctx.exec_ctx, + &tempfile, + url, + help_on_error, + ), + Some(other) => panic!("unsupported protocol {other} in {url}"), + None => panic!("no protocol in {url}"), + } + t!(move_file(&tempfile, dest_path), format!("failed to rename {tempfile:?} to {dest_path:?}")); +} + +/// Create a temporary directory in `out` and return its path. +/// +/// NOTE: this temporary directory is shared between all steps; +/// if you need an empty directory, create a new subdirectory inside it. +pub(crate) fn tempdir(out: &Path) -> PathBuf { + let tmp = out.join("tmp"); + t!(fs::create_dir_all(&tmp)); + tmp +} + +fn download_http_with_retries( + host_target: TargetSelection, + is_running_on_ci: bool, + exec_ctx: &ExecutionContext, + tempfile: &Path, + url: &str, + help_on_error: &str, +) { + println!("downloading {url}"); + // Try curl. If that fails and we are on windows, fallback to PowerShell. + // options should be kept in sync with + // src/bootstrap/src/core/download.rs + // for consistency + let mut curl = command("curl").allow_failure(); + curl.args([ + // follow redirect + "--location", + // timeout if speed is < 10 bytes/sec for > 30 seconds + "--speed-time", + "30", + "--speed-limit", + "10", + // timeout if cannot connect within 30 seconds + "--connect-timeout", + "30", + // output file + "--output", + tempfile.to_str().unwrap(), + // if there is an error, don't restart the download, + // instead continue where it left off. + "--continue-at", + "-", + // retry up to 3 times. note that this means a maximum of 4 + // attempts will be made, since the first attempt isn't a *re*try. + "--retry", + "3", + // show errors, even if --silent is specified + "--show-error", + // set timestamp of downloaded file to that of the server + "--remote-time", + // fail on non-ok http status + "--fail", + ]); + // Don't print progress in CI; the \r wrapping looks bad and downloads don't take long enough for progress to be useful. + if is_running_on_ci { + curl.arg("--silent"); + } else { + curl.arg("--progress-bar"); + } + // --retry-all-errors was added in 7.71.0, don't use it if curl is old. + if curl_version(exec_ctx) >= semver::Version::new(7, 71, 0) { + curl.arg("--retry-all-errors"); + } + curl.arg(url); + if !curl.run(exec_ctx) { + if host_target.contains("windows-msvc") { + eprintln!("Fallback to PowerShell"); + for _ in 0..3 { + let powershell = command("PowerShell.exe").allow_failure().args([ + "/nologo", + "-Command", + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;", + &format!( + "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')", + url, tempfile.to_str().expect("invalid UTF-8 not supported with powershell downloads"), + ), + ]).run_capture_stdout(exec_ctx); + + if powershell.is_failure() { + return; + } + + eprintln!("\nspurious failure, trying again"); + } + } + if !help_on_error.is_empty() { + eprintln!("{help_on_error}"); + } + crate::exit!(1); + } +} + +fn curl_version(exec_ctx: &ExecutionContext) -> semver::Version { + let mut curl = command("curl"); + curl.arg("-V"); + let curl = curl.run_capture_stdout(exec_ctx); + if curl.is_failure() { + return semver::Version::new(1, 0, 0); + } + let output = curl.stdout(); + extract_curl_version(output) +} From 8facd063f93d5640790b200fca4454d2c76c5305 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 27 Jul 2025 12:16:03 +0530 Subject: [PATCH 084/201] move download_beta_toolchain out of impl as its used during config initialization --- src/bootstrap/src/core/download.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index f3a10048b655..7e4ae6ebd7e8 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -491,6 +491,31 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo } } +#[cfg(test)] +pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef>) {} + +#[cfg(not(test))] +pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef>) { + let dwn_ctx = dwn_ctx.as_ref(); + if dwn_ctx.verbose { + println!("downloading stage0 beta artifacts"); + } + + let date = dwn_ctx.stage0_metadata.compiler.date.clone(); + let version = dwn_ctx.stage0_metadata.compiler.version.clone(); + let extra_components = ["cargo"]; + let sysroot = "stage0"; + download_toolchain( + dwn_ctx, + &version, + sysroot, + &date, + &extra_components, + "stage0", + DownloadSource::Dist, + ); +} + fn download_toolchain<'a>( dwn_ctx: impl AsRef>, version: &str, From 19dc195ac2db97874020a423e690d1edf7f1d8fe Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 27 Jul 2025 12:17:11 +0530 Subject: [PATCH 085/201] move download_rustfmt out of impl as its used during config initialization --- src/bootstrap/src/core/download.rs | 66 ++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 7e4ae6ebd7e8..745acb04275a 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -491,6 +491,72 @@ pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: boo } } +#[cfg(test)] +pub(crate) fn maybe_download_rustfmt<'a>( + dwn_ctx: impl AsRef>, +) -> Option { + Some(PathBuf::new()) +} + +/// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't +/// reuse target directories or artifacts +#[cfg(not(test))] +pub(crate) fn maybe_download_rustfmt<'a>( + dwn_ctx: impl AsRef>, +) -> Option { + use build_helper::stage0_parser::VersionMetadata; + + let dwn_ctx = dwn_ctx.as_ref(); + + if dwn_ctx.dry_run { + return Some(PathBuf::new()); + } + + let VersionMetadata { date, version } = dwn_ctx.stage0_metadata.rustfmt.as_ref()?; + let channel = format!("{version}-{date}"); + + let host = dwn_ctx.host_target; + let bin_root = dwn_ctx.out.join(host).join("rustfmt"); + let rustfmt_path = bin_root.join("bin").join(exe("rustfmt", host)); + let rustfmt_stamp = BuildStamp::new(&bin_root).with_prefix("rustfmt").add_stamp(channel); + if rustfmt_path.exists() && rustfmt_stamp.is_up_to_date() { + return Some(rustfmt_path); + } + + download_component( + dwn_ctx, + DownloadSource::Dist, + format!("rustfmt-{version}-{build}.tar.xz", build = host.triple), + "rustfmt-preview", + date, + "rustfmt", + ); + + download_component( + dwn_ctx, + DownloadSource::Dist, + format!("rustc-{version}-{build}.tar.xz", build = host.triple), + "rustc", + date, + "rustfmt", + ); + + if should_fix_bins_and_dylibs(dwn_ctx.patch_binaries_for_nix, dwn_ctx.exec_ctx) { + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("rustfmt"), dwn_ctx.exec_ctx); + fix_bin_or_dylib(dwn_ctx.out, &bin_root.join("bin").join("cargo-fmt"), dwn_ctx.exec_ctx); + let lib_dir = bin_root.join("lib"); + for lib in t!(fs::read_dir(&lib_dir), lib_dir.display().to_string()) { + let lib = t!(lib); + if path_is_dylib(&lib.path()) { + fix_bin_or_dylib(dwn_ctx.out, &lib.path(), dwn_ctx.exec_ctx); + } + } + } + + t!(rustfmt_stamp.write()); + Some(rustfmt_path) +} + #[cfg(test)] pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef>) {} From ad4b8694df3cba7b1651c75a18761c0a9a2c280a Mon Sep 17 00:00:00 2001 From: klensy Date: Sat, 26 Jul 2025 12:04:32 +0300 Subject: [PATCH 086/201] bump cargo_metadata --- Cargo.lock | 18 ++---------------- src/tools/generate-copyright/Cargo.toml | 2 +- .../generate-copyright/src/cargo_metadata.rs | 3 ++- src/tools/tidy/Cargo.toml | 2 +- src/tools/tidy/src/deps.rs | 16 ++++++++-------- 5 files changed, 14 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d37e8b2f84c..efd9352f114a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -441,20 +441,6 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "cargo_metadata" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" -dependencies = [ - "camino", - "cargo-platform 0.1.9", - "semver", - "serde", - "serde_json", - "thiserror 2.0.12", -] - [[package]] name = "cargo_metadata" version = "0.21.0" @@ -1364,7 +1350,7 @@ version = "0.1.0" dependencies = [ "anyhow", "askama", - "cargo_metadata 0.18.1", + "cargo_metadata 0.21.0", "serde", "serde_json", "thiserror 1.0.69", @@ -5369,7 +5355,7 @@ name = "tidy" version = "0.1.0" dependencies = [ "build_helper", - "cargo_metadata 0.19.2", + "cargo_metadata 0.21.0", "fluent-syntax", "ignore", "miropt-test-tools", diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index e420a450d422..bcb3165de458 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -9,7 +9,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" askama = "0.14.0" -cargo_metadata = "0.18.1" +cargo_metadata = "0.21" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" thiserror = "1" diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs index 3fae26bda475..87cd85c8def4 100644 --- a/src/tools/generate-copyright/src/cargo_metadata.rs +++ b/src/tools/generate-copyright/src/cargo_metadata.rs @@ -92,7 +92,8 @@ pub fn get_metadata( continue; } // otherwise it's an out-of-tree dependency - let package_id = Package { name: package.name, version: package.version.to_string() }; + let package_id = + Package { name: package.name.to_string(), version: package.version.to_string() }; output.insert( package_id, PackageMetadata { diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml index d995106ae025..c1f27de7ed4a 100644 --- a/src/tools/tidy/Cargo.toml +++ b/src/tools/tidy/Cargo.toml @@ -6,7 +6,7 @@ autobins = false [dependencies] build_helper = { path = "../../build_helper" } -cargo_metadata = "0.19" +cargo_metadata = "0.21" regex = "1" miropt-test-tools = { path = "../miropt-test-tools" } walkdir = "2" diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 21cbb143c511..dbcac51d375f 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -633,8 +633,8 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut b proc_macro_deps.retain(|pkg| !is_proc_macro_pkg(&metadata[pkg])); let proc_macro_deps: HashSet<_> = - proc_macro_deps.into_iter().map(|dep| metadata[dep].name.clone()).collect(); - let expected = proc_macro_deps::CRATES.iter().map(|s| s.to_string()).collect::>(); + proc_macro_deps.into_iter().map(|dep| metadata[dep].name.as_ref()).collect(); + let expected = proc_macro_deps::CRATES.iter().copied().collect::>(); let needs_blessing = proc_macro_deps.difference(&expected).next().is_some() || expected.difference(&proc_macro_deps).next().is_some(); @@ -718,7 +718,7 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { // See https://github.com/rust-lang/rust/issues/62620 for more. // In general, these should never be added and this exception // should not be taken as precedent for any new target. - if pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") { + if *pkg.name == "fortanix-sgx-abi" && pkg.license.as_deref() == Some("MPL-2.0") { continue; } @@ -734,7 +734,7 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba // Validate the EXCEPTIONS list hasn't changed. for (name, license) in exceptions { // Check that the package actually exists. - if !metadata.packages.iter().any(|p| p.name == *name) { + if !metadata.packages.iter().any(|p| *p.name == *name) { tidy_error!( bad, "could not find exception package `{}`\n\ @@ -743,7 +743,7 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba ); } // Check that the license hasn't changed. - for pkg in metadata.packages.iter().filter(|p| p.name == *name) { + for pkg in metadata.packages.iter().filter(|p| *p.name == *name) { match &pkg.license { None => { if *license == NON_STANDARD_LICENSE @@ -818,9 +818,9 @@ fn check_permitted_dependencies( let Ok(version) = Version::parse(version) else { return false; }; - pkg.name == name && pkg.version == version + *pkg.name == name && pkg.version == version } else { - pkg.name == permitted + *pkg.name == permitted } } if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) { @@ -868,7 +868,7 @@ fn check_permitted_dependencies( /// Finds a package with the given name. fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package { - let mut i = metadata.packages.iter().filter(|p| p.name == name); + let mut i = metadata.packages.iter().filter(|p| *p.name == name); let result = i.next().unwrap_or_else(|| panic!("could not find package `{name}` in package list")); assert!(i.next().is_none(), "more than one package found for `{name}`"); From 23e294262bb5a03eb12ae9d0724c7efdc4516b17 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 12:13:24 +0200 Subject: [PATCH 087/201] call_function helper: dont ICE on return type mismatches --- src/tools/miri/src/helpers.rs | 5 ++- .../tests/fail/shims/ctor_wrong_ret_type.rs | 39 +++++++++++++++++++ .../fail/shims/ctor_wrong_ret_type.stderr | 12 ++++++ 3 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs create mode 100644 src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 6e80bc5da9eb..53e4e5c4c533 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -437,7 +437,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// For now, arguments must be scalars (so that the caller does not have to know the layout). /// /// If you do not provide a return place, a dangling zero-sized place will be created - /// for your convenience. + /// for your convenience. This is only valid if the return type is `()`. fn call_function( &mut self, f: ty::Instance<'tcx>, @@ -452,7 +452,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => dest.clone(), - None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?), + None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit), }; // Construct a function pointer type representing the caller perspective. @@ -465,6 +465,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + // This will also show proper errors if there is any ABI mismatch. this.init_stack_frame( f, mir, diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs new file mode 100644 index 000000000000..1e10f682e71e --- /dev/null +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs @@ -0,0 +1,39 @@ +unsafe extern "C" fn ctor() -> i32 { + //~^ERROR: calling a function with return type i32 passing return place of type () + 0 +} + +#[rustfmt::skip] +macro_rules! ctor { + ($ident:ident = $ctor:ident) => { + #[cfg_attr( + all(any( + target_os = "linux", + target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "haiku", + target_os = "illumos", + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris", + target_os = "none", + target_family = "wasm", + )), + link_section = ".init_array" + )] + #[cfg_attr(windows, link_section = ".CRT$XCU")] + #[cfg_attr( + any(target_os = "macos", target_os = "ios"), + // We do not set the `mod_init_funcs` flag here since ctor/inventory also do not do + // that. See . + link_section = "__DATA,__mod_init_func" + )] + #[used] + static $ident: unsafe extern "C" fn() -> i32 = $ctor; + }; +} + +ctor! { CTOR = ctor } + +fn main() {} diff --git a/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr new file mode 100644 index 000000000000..664bfbd32db2 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr @@ -0,0 +1,12 @@ +error: Undefined Behavior: calling a function with return type i32 passing return place of type () + | + = note: Undefined Behavior occurred here + = note: (no span available) + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri + = note: BACKTRACE: + +error: aborting due to 1 previous error + From 49eda8edd5c99e4c65c687fff0b8e194eb339a23 Mon Sep 17 00:00:00 2001 From: godzie44 Date: Thu, 24 Jul 2025 16:30:22 +0300 Subject: [PATCH 088/201] fix(debuginfo): disable overflow check for recursive non-enum types --- .../src/debuginfo/metadata/type_map.rs | 4 +-- .../debuginfo-cyclic-structure.rs | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/debuginfo-cyclic-structure.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs index 56fb12d3c22e..d1502d2b1e62 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs @@ -285,8 +285,8 @@ pub(super) fn build_type_with_children<'ll, 'tcx>( // Item(T), // } // ``` - let is_expanding_recursive = - debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { + let is_expanding_recursive = adt_def.is_enum() + && debug_context(cx).adt_stack.borrow().iter().any(|(parent_def_id, parent_args)| { if def_id == *parent_def_id { args.iter().zip(parent_args.iter()).any(|(arg, parent_arg)| { if let (Some(arg), Some(parent_arg)) = (arg.as_type(), parent_arg.as_type()) diff --git a/tests/codegen-llvm/debuginfo-cyclic-structure.rs b/tests/codegen-llvm/debuginfo-cyclic-structure.rs new file mode 100644 index 000000000000..b8cc54477415 --- /dev/null +++ b/tests/codegen-llvm/debuginfo-cyclic-structure.rs @@ -0,0 +1,32 @@ +//@ compile-flags:-g -Copt-level=0 -C panic=abort + +// Check that debug information exists for structures containing loops (cyclic references). +// Previously it may incorrectly prune member information during recursive type inference check. + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Arc { + buffer: Box>, +} +struct Shared { + shared: Arc>>, +} +struct Handle { + shared: Shared, +} +struct Core { + inner: Arc>>, +} + +#[no_mangle] +extern "C" fn test() { + let с = Core { inner: Arc::new(Inner { buffer: Box::new(MaybeUninit::uninit()) }) }; + std::hint::black_box(с); +} From 24e2b4832bfa0bf1b60159af0ae11b15de55590e Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 27 Jul 2025 21:19:07 +1000 Subject: [PATCH 089/201] coverage: Infer `instances_used` from `pgo_func_name_var_map` In obscure circumstances, we would sometimes emit a covfun record for a function with no physical coverage counters, causing `llvm-cov` to fail with the cryptic error message: malformed instrumentation profile data: function name is empty We can eliminate this mismatch by removing `instances_used` entirely, and instead inferring its contents from the keys of `pgo_func_name_var_map`. This makes it impossible for a "used" function to lack a PGO name entry. --- .../src/coverageinfo/mapgen.rs | 12 +++------ .../src/coverageinfo/mod.rs | 27 ++++++++++++------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index a9be833a6439..8c9dfcfd18c2 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -46,21 +46,17 @@ pub(crate) fn finalize(cx: &mut CodegenCx<'_, '_>) { debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name()); // FIXME(#132395): Can this be none even when coverage is enabled? - let instances_used = match cx.coverage_cx { - Some(ref cx) => cx.instances_used.borrow(), - None => return, - }; + let Some(ref coverage_cx) = cx.coverage_cx else { return }; - let mut covfun_records = instances_used - .iter() - .copied() + let mut covfun_records = coverage_cx + .instances_used() + .into_iter() // Sort by symbol name, so that the global file table is built in an // order that doesn't depend on the stable-hash-based order in which // instances were visited during codegen. .sorted_by_cached_key(|&instance| tcx.symbol_name(instance).name) .filter_map(|instance| prepare_covfun_record(tcx, instance, true)) .collect::>(); - drop(instances_used); // In a single designated CGU, also prepare covfun records for functions // in this crate that were instrumented for coverage, but are unused. diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index eefbd7cf6c48..35c322326f8e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -5,7 +5,7 @@ use rustc_abi::Size; use rustc_codegen_ssa::traits::{ BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use tracing::{debug, instrument}; @@ -20,9 +20,14 @@ mod mapgen; /// Extra per-CGU context/state needed for coverage instrumentation. pub(crate) struct CguCoverageContext<'ll, 'tcx> { - /// Coverage data for each instrumented function identified by DefId. - pub(crate) instances_used: RefCell>>, - pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, + /// Associates function instances with an LLVM global that holds the + /// function's symbol name, as needed by LLVM coverage intrinsics. + /// + /// Instances in this map are also considered "used" for the purposes of + /// emitting covfun records. Every covfun record holds a hash of its + /// symbol name, and `llvm-cov` will exit fatally if it can't resolve that + /// hash back to an entry in the binary's `__llvm_prf_names` linker section. + pub(crate) pgo_func_name_var_map: RefCell, &'ll llvm::Value>>, pub(crate) mcdc_condition_bitmap_map: RefCell, Vec<&'ll llvm::Value>>>, covfun_section_name: OnceCell, @@ -31,7 +36,6 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> { impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { pub(crate) fn new() -> Self { Self { - instances_used: RefCell::>::default(), pgo_func_name_var_map: Default::default(), mcdc_condition_bitmap_map: Default::default(), covfun_section_name: Default::default(), @@ -53,6 +57,14 @@ impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> { .and_then(|bitmap_map| bitmap_map.get(decision_depth as usize)) .copied() // Dereference Option<&&Value> to Option<&Value> } + + /// Returns the list of instances considered "used" in this CGU, as + /// inferred from the keys of `pgo_func_name_var_map`. + pub(crate) fn instances_used(&self) -> Vec> { + // Collecting into a Vec is way easier than trying to juggle RefCell + // projections, and this should only run once per CGU anyway. + self.pgo_func_name_var_map.borrow().keys().copied().collect::>() + } } impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { @@ -151,11 +163,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; }; - // Mark the instance as used in this CGU, for coverage purposes. - // This includes functions that were not partitioned into this CGU, - // but were MIR-inlined into one of this CGU's functions. - coverage_cx.instances_used.borrow_mut().insert(instance); - match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" From 89b6b0b6a482cb0d632f54f14936def9c034570d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 27 Jul 2025 21:29:48 +1000 Subject: [PATCH 090/201] coverage: Clarify that getting a PGO name also makes a function "used" --- compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 35c322326f8e..119237abd6b8 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -90,7 +90,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { /// string, to hold the function name passed to LLVM intrinsic /// `instrprof.increment()`. The `Value` is only created once per instance. /// Multiple invocations with the same instance return the same `Value`. - fn get_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { + /// + /// This has the side-effect of causing coverage codegen to consider this + /// function "used", making it eligible to emit an associated covfun record. + fn ensure_pgo_func_name_var(&self, instance: Instance<'tcx>) -> &'ll llvm::Value { debug!("getting pgo_func_name_var for instance={:?}", instance); let mut pgo_func_name_var_map = self.coverage_cx().pgo_func_name_var_map.borrow_mut(); pgo_func_name_var_map.entry(instance).or_insert_with(|| { @@ -114,7 +117,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { return; } - let fn_name = self.get_pgo_func_name_var(instance); + let fn_name = self.ensure_pgo_func_name_var(instance); let hash = self.const_u64(function_coverage_info.function_source_hash); let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32); self.mcdc_parameters(fn_name, hash, bitmap_bits); @@ -170,7 +173,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { CoverageKind::VirtualCounter { bcb } if let Some(&id) = ids_info.phys_counter_for_node.get(&bcb) => { - let fn_name = bx.get_pgo_func_name_var(instance); + let fn_name = bx.ensure_pgo_func_name_var(instance); let hash = bx.const_u64(function_coverage_info.function_source_hash); let num_counters = bx.const_u32(ids_info.num_counters); let index = bx.const_u32(id.as_u32()); @@ -200,7 +203,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { "bitmap index of the decision out of range" ); - let fn_name = bx.get_pgo_func_name_var(instance); + let fn_name = bx.ensure_pgo_func_name_var(instance); let hash = bx.const_u64(function_coverage_info.function_source_hash); let bitmap_index = bx.const_u32(bitmap_idx); bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap); From fd65b7e6646edd72549bf83386f381ba2cf7fc0b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 13:34:55 +0200 Subject: [PATCH 091/201] introduce a macro for shim signature checking Co-Authored-By: geetanshjuneja --- src/tools/miri/src/helpers.rs | 213 +-------- src/tools/miri/src/intrinsics/atomic.rs | 2 +- src/tools/miri/src/intrinsics/mod.rs | 22 +- src/tools/miri/src/intrinsics/simd.rs | 5 +- src/tools/miri/src/lib.rs | 2 + src/tools/miri/src/shims/aarch64.rs | 5 +- src/tools/miri/src/shims/backtrace.rs | 8 +- src/tools/miri/src/shims/foreign_items.rs | 90 ++-- src/tools/miri/src/shims/mod.rs | 1 + src/tools/miri/src/shims/sig.rs | 265 ++++++++++ .../src/shims/unix/android/foreign_items.rs | 11 +- .../miri/src/shims/unix/android/thread.rs | 4 +- src/tools/miri/src/shims/unix/fd.rs | 2 +- .../miri/src/shims/unix/foreign_items.rs | 451 +++++++----------- .../src/shims/unix/freebsd/foreign_items.rs | 26 +- src/tools/miri/src/shims/unix/fs.rs | 2 +- .../src/shims/unix/linux/foreign_items.rs | 39 +- .../miri/src/shims/unix/linux_like/sync.rs | 2 +- .../miri/src/shims/unix/linux_like/syscall.rs | 4 +- .../src/shims/unix/macos/foreign_items.rs | 71 +-- .../src/shims/unix/solarish/foreign_items.rs | 34 +- src/tools/miri/src/shims/unwind.rs | 6 +- .../miri/src/shims/wasi/foreign_items.rs | 6 +- .../miri/src/shims/windows/foreign_items.rs | 147 +++--- src/tools/miri/src/shims/x86/aesni.rs | 14 +- src/tools/miri/src/shims/x86/avx.rs | 50 +- src/tools/miri/src/shims/x86/avx2.rs | 57 ++- src/tools/miri/src/shims/x86/bmi.rs | 2 +- src/tools/miri/src/shims/x86/gfni.rs | 9 +- src/tools/miri/src/shims/x86/mod.rs | 11 +- src/tools/miri/src/shims/x86/sha.rs | 6 +- src/tools/miri/src/shims/x86/sse.rs | 24 +- src/tools/miri/src/shims/x86/sse2.rs | 40 +- src/tools/miri/src/shims/x86/sse3.rs | 5 +- src/tools/miri/src/shims/x86/sse41.rs | 28 +- src/tools/miri/src/shims/x86/sse42.rs | 14 +- src/tools/miri/src/shims/x86/ssse3.rs | 17 +- 37 files changed, 900 insertions(+), 795 deletions(-) create mode 100644 src/tools/miri/src/shims/sig.rs diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 6e80bc5da9eb..0c96ddf00db4 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -3,7 +3,7 @@ use std::time::Duration; use std::{cmp, iter}; use rand::RngCore; -use rustc_abi::{Align, CanonAbi, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; +use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants}; use rustc_apfloat::Float; use rustc_apfloat::ieee::{Double, Half, Quad, Single}; use rustc_hir::Safety; @@ -14,11 +14,10 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout}; -use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy}; +use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy}; use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::callconv::FnAbi; use crate::*; @@ -437,7 +436,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// For now, arguments must be scalars (so that the caller does not have to know the layout). /// /// If you do not provide a return place, a dangling zero-sized place will be created - /// for your convenience. + /// for your convenience. This is only valid if the return type is `()`. fn call_function( &mut self, f: ty::Instance<'tcx>, @@ -452,7 +451,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let mir = this.load_mir(f.def, None)?; let dest = match dest { Some(dest) => dest.clone(), - None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?), + None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit), }; // Construct a function pointer type representing the caller perspective. @@ -465,6 +464,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?; + // This will also show proper errors if there is any ABI mismatch. this.init_stack_frame( f, mir, @@ -929,21 +929,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi) } - /// Check that the calling convention is what we expect. - fn check_callconv<'a>( - &self, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - ) -> InterpResult<'a, ()> { - if fn_abi.conv != exp_abi { - throw_ub_format!( - r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, - fn_abi.conv - ); - } - interp_ok(()) - } - fn frame_in_std(&self) -> bool { let this = self.eval_context_ref(); let frame = this.frame(); @@ -967,161 +952,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { crate_name == "std" || crate_name == "std_miri_test" } - fn check_abi_and_shim_symbol_clash( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - ) -> InterpResult<'tcx, ()> { - self.check_callconv(abi, exp_abi)?; - if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? { - // If compiler-builtins is providing the symbol, then don't treat it as a clash. - // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased - // performance. Note that this means we won't catch any undefined behavior in - // compiler-builtins when running other crates, but Miri can still be run on - // compiler-builtins itself (or any crate that uses it as a normal dependency) - if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) { - return interp_ok(()); - } - - throw_machine_stop!(TerminationInfo::SymbolShimClashing { - link_name, - span: body.span.data(), - }) - } - interp_ok(()) - } - - fn check_shim<'a, const N: usize>( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; - - if abi.c_variadic { - throw_ub_format!( - "calling a non-variadic function with a variadic caller-side signature" - ); - } - if let Ok(ops) = args.try_into() { - return interp_ok(ops); - } - throw_ub_format!( - "incorrect number of arguments for `{link_name}`: got {}, expected {}", - args.len(), - N - ) - } - - /// Check that the given `caller_fn_abi` matches the expected ABI described by - /// `callee_abi`, `callee_input_tys`, `callee_output_ty`, and then returns the list of - /// arguments. - fn check_shim_abi<'a, const N: usize>( - &mut self, - link_name: Symbol, - caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, - callee_abi: ExternAbi, - callee_input_tys: [Ty<'tcx>; N], - callee_output_ty: Ty<'tcx>, - caller_args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - let this = self.eval_context_mut(); - let mut inputs_and_output = callee_input_tys.to_vec(); - inputs_and_output.push(callee_output_ty); - let fn_sig_binder = Binder::dummy(FnSig { - inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), - c_variadic: false, - // This does not matter for the ABI. - safety: Safety::Safe, - abi: callee_abi, - }); - let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; - - this.check_abi_and_shim_symbol_clash(caller_fn_abi, callee_fn_abi.conv, link_name)?; - - if caller_fn_abi.c_variadic { - throw_ub_format!( - "ABI mismatch: calling a non-variadic function with a variadic caller-side signature" - ); - } - - if callee_fn_abi.fixed_count != caller_fn_abi.fixed_count { - throw_ub_format!( - "ABI mismatch: expected {} arguments, found {} arguments ", - callee_fn_abi.fixed_count, - caller_fn_abi.fixed_count - ); - } - - if callee_fn_abi.can_unwind && !caller_fn_abi.can_unwind { - throw_ub_format!( - "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", - ); - } - - if !this.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? { - throw_ub!(AbiMismatchReturn { - caller_ty: caller_fn_abi.ret.layout.ty, - callee_ty: callee_fn_abi.ret.layout.ty - }); - } - - if let Some(index) = caller_fn_abi - .args - .iter() - .zip(callee_fn_abi.args.iter()) - .map(|(caller_arg, callee_arg)| this.check_argument_compat(caller_arg, callee_arg)) - .collect::>>()? - .into_iter() - .position(|b| !b) - { - throw_ub!(AbiMismatchArgument { - caller_ty: caller_fn_abi.args[index].layout.ty, - callee_ty: callee_fn_abi.args[index].layout.ty - }); - } - - if let Ok(ops) = caller_args.try_into() { - return interp_ok(ops); - } - unreachable!() - } - - /// Check shim for variadic function. - /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. - fn check_shim_variadic<'a, const N: usize>( - &mut self, - abi: &FnAbi<'tcx, Ty<'tcx>>, - exp_abi: CanonAbi, - link_name: Symbol, - args: &'a [OpTy<'tcx>], - ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> - where - &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, - { - self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?; - - if !abi.c_variadic { - throw_ub_format!( - "calling a variadic function with a non-variadic caller-side signature" - ); - } - if abi.fixed_count != u32::try_from(N).unwrap() { - throw_ub_format!( - "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}", - link_name.as_str(), - abi.fixed_count - ) - } - if let Some(args) = args.split_first_chunk() { - return interp_ok(args); - } - panic!("mismatch between signature and `args` slice"); - } - /// Mark a machine allocation that was just created as immutable. fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) { let this = self.eval_context_mut(); @@ -1317,39 +1147,6 @@ impl<'tcx> MiriMachine<'tcx> { } } -/// Check that the number of args is what we expect. -pub fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>( - args: &'a [OpTy<'tcx>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> -where - &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, -{ - if let Ok(ops) = args.try_into() { - return interp_ok(ops); - } - throw_ub_format!( - "incorrect number of arguments for intrinsic: got {}, expected {}", - args.len(), - N - ) -} - -/// Check that the number of varargs is at least the minimum what we expect. -/// Fixed args should not be included. -pub fn check_min_vararg_count<'a, 'tcx, const N: usize>( - name: &'a str, - args: &'a [OpTy<'tcx>], -) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { - if let Some((ops, _)) = args.split_first_chunk() { - return interp_ok(ops); - } - throw_ub_format!( - "not enough variadic arguments for `{name}`: got {}, expected at least {}", - args.len(), - N - ) -} - pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> { throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!( "{name} not available when isolation is enabled", diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 0a59a707a101..bcc3e9ec885f 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -2,7 +2,7 @@ use rustc_middle::mir::BinOp; use rustc_middle::ty::AtomicOrdering; use rustc_middle::{mir, ty}; -use self::helpers::check_intrinsic_arg_count; +use super::check_intrinsic_arg_count; use crate::*; pub enum AtomicOp { diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 4efa7dd4dcf8..b5e81460773b 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -14,11 +14,28 @@ use rustc_middle::ty::{self, FloatTy, ScalarInt}; use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; -use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count}; +use self::helpers::{ToHost, ToSoft}; use self::simd::EvalContextExt as _; use crate::math::{IeeeExt, apply_random_float_error_ulp}; use crate::*; +/// Check that the number of args is what we expect. +fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>( + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> +where + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, +{ + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + throw_ub_format!( + "incorrect number of arguments for intrinsic: got {}, expected {}", + args.len(), + N + ) +} + impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn call_intrinsic( @@ -114,7 +131,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )); } "catch_unwind" => { - this.handle_catch_unwind(args, dest, ret)?; + let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?; + this.handle_catch_unwind(try_fn, data, catch_fn, dest, ret)?; // This pushed a stack frame, don't jump to `ret`. return interp_ok(EmulateItemResult::AlreadyJumped); } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index e63992aa95f9..b26516c0ff0e 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -6,9 +6,8 @@ use rustc_middle::ty::FloatTy; use rustc_middle::{mir, ty}; use rustc_span::{Symbol, sym}; -use crate::helpers::{ - ToHost, ToSoft, bool_to_simd_element, check_intrinsic_arg_count, simd_element_to_bool, -}; +use super::check_intrinsic_arg_count; +use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, simd_element_to_bool}; use crate::*; #[derive(Copy, Clone)] diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index ae70257653c8..507d4f7b4289 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -7,6 +7,7 @@ #![feature(never_type)] #![feature(try_blocks)] #![feature(io_error_more)] +#![feature(if_let_guard)] #![feature(variant_count)] #![feature(yeet_expr)] #![feature(nonzero_ops)] @@ -158,6 +159,7 @@ pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _}; pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError}; pub use crate::shims::os_str::EvalContextExt as _; pub use crate::shims::panic::EvalContextExt as _; +pub use crate::shims::sig::EvalContextExt as _; pub use crate::shims::time::EvalContextExt as _; pub use crate::shims::tls::TlsData; pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _}; diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index 44ad5081ad57..6e422b4ab716 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -20,7 +20,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap(); match unprefixed_name { "isb" => { - let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; match arg { // SY ("full system scope") @@ -38,7 +38,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `left` input, the second half of the output from the `right` input. // https://developer.arm.com/architectures/instruction-sets/intrinsics/vpmaxq_u8 "neon.umaxp.v16i8" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index 18d60915d205..bd3914b652ac 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -15,7 +15,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { @@ -37,7 +37,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; let ptr_layout = this.layout_of(ptr_ty)?; - let [flags, buf] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [flags, buf] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; let buf_place = this.deref_pointer_as(buf, ptr_layout)?; @@ -117,7 +117,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); - let [ptr, flags] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; @@ -195,7 +195,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let this = self.eval_context_mut(); let [ptr, flags, name_ptr, filename_ptr] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let flags = this.read_scalar(flags)?.to_u64()?; if flags != 0 { diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 94cda57658a1..21545b680299 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -288,16 +288,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Miri-specific extern functions "miri_start_unwind" => { - let [payload] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [payload] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; this.run_provenance_gc(); } "miri_get_alloc_id" => { - let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| { err_machine_stop!(TerminationInfo::Abort(format!( @@ -307,7 +308,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?; } "miri_print_borrow_state" => { - let [id, show_unnamed] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [id, show_unnamed] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let id = this.read_scalar(id)?.to_u64()?; let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?; if let Some(id) = std::num::NonZero::new(id).map(AllocId) @@ -322,7 +324,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // This associates a name to a tag. Very useful for debugging, and also makes // tests more strict. let [ptr, nth_parent, name] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nth_parent = this.read_scalar(nth_parent)?.to_u8()?; let name = this.read_immediate(name)?; @@ -335,7 +337,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.give_pointer_debug_name(ptr, nth_parent, &name)?; } "miri_static_root" => { - let [ptr] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?; if offset != Size::ZERO { @@ -346,7 +348,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.machine.static_roots.push(alloc_id); } "miri_host_to_target_path" => { - let [ptr, out, out_size] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, out, out_size] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let out = this.read_pointer(out)?; let out_size = this.read_scalar(out_size)?.to_target_usize(this)?; @@ -382,7 +385,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Writes some bytes to the interpreter's stdout/stderr. See the // README for details. "miri_write_to_stdout" | "miri_write_to_stderr" => { - let [msg] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [msg] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let msg = this.read_immediate(msg)?; let msg = this.read_byte_slice(&msg)?; // Note: we're ignoring errors writing to host stdout/stderr. @@ -396,7 +399,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { "miri_promise_symbolic_alignment" => { use rustc_abi::AlignFromBytesError; - let [ptr, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [ptr, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let align = this.read_target_usize(align)?; if !align.is_power_of_two() { @@ -437,12 +441,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Aborting the process. "exit" => { - let [code] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); } "abort" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; throw_machine_stop!(TerminationInfo::Abort( "the program aborted execution".to_owned() )) @@ -450,7 +454,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Standard C allocation "malloc" => { - let [size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [size] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { let res = this.malloc(size, AllocInit::Uninit)?; @@ -464,7 +468,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "calloc" => { - let [items, elem_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [items, elem_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { @@ -479,12 +484,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "free" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; this.free(ptr)?; } "realloc" => { - let [old_ptr, new_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [old_ptr, new_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let old_ptr = this.read_pointer(old_ptr)?; let new_size = this.read_target_usize(new_size)?; if new_size <= this.max_size_of_val().bytes() { @@ -504,7 +510,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let default = |ecx: &mut MiriInterpCx<'tcx>| { // Only call `check_shim` when `#[global_allocator]` isn't used. When that // macro is used, we act like no shim exists, so that the exported function can run. - let [size, align] = ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [size, align] = + ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let size = ecx.read_target_usize(size)?; let align = ecx.read_target_usize(align)?; @@ -537,7 +544,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.emulate_allocator(|this| { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. - let [size, align] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [size, align] = + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let size = this.read_target_usize(size)?; let align = this.read_target_usize(align)?; @@ -559,7 +567,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. let [ptr, old_size, align] = - ecx.check_shim(abi, CanonAbi::Rust, link_name, args)?; + ecx.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = ecx.read_pointer(ptr)?; let old_size = ecx.read_target_usize(old_size)?; let align = ecx.read_target_usize(align)?; @@ -590,7 +598,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // See the comment for `__rust_alloc` why `check_shim` is only called in the // default case. let [ptr, old_size, align, new_size] = - this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let ptr = this.read_pointer(ptr)?; let old_size = this.read_target_usize(old_size)?; let align = this.read_target_usize(align)?; @@ -613,20 +621,21 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } name if name == this.mangle_internal_symbol("__rust_no_alloc_shim_is_unstable_v2") => { // This is a no-op shim that only exists to prevent making the allocator shims instantly stable. - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; } name if name == this.mangle_internal_symbol("__rust_alloc_error_handler_should_panic_v2") => { // Gets the value of the `oom` option. - let [] = this.check_shim(abi, CanonAbi::Rust, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?; let val = this.tcx.sess.opts.unstable_opts.oom.should_panic(); this.write_int(val, dest)?; } // C memory handling functions "memcmp" => { - let [left, right, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_pointer(left)?; let right = this.read_pointer(right)?; let n = Size::from_bytes(this.read_target_usize(n)?); @@ -650,7 +659,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(result), dest)?; } "memrchr" => { - let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, val, num] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; @@ -676,7 +686,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "memchr" => { - let [ptr, val, num] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, val, num] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let val = this.read_scalar(val)?.to_i32()?; let num = this.read_target_usize(num)?; @@ -699,7 +710,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } } "strlen" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_c_str(ptr)?.len(); @@ -709,7 +720,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "wcslen" => { - let [ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; // This reads at least 1 byte, so we are already enforcing that this is a valid pointer. let n = this.read_wchar_t_str(ptr)?.len(); @@ -719,7 +730,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "memcpy" => { - let [ptr_dest, ptr_src, n] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src, n] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; let n = this.read_target_usize(n)?; @@ -733,7 +745,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } "strcpy" => { - let [ptr_dest, ptr_src] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr_dest, ptr_src] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; @@ -764,7 +777,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erff" | "erfcf" => { - let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f32()?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); @@ -802,7 +815,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2f" | "fdimf" => { - let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f32()?; let f2 = this.read_scalar(f2)?.to_f32()?; // underscore case for windows, here and below @@ -841,7 +854,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "erf" | "erfc" => { - let [f] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f = this.read_scalar(f)?.to_f64()?; // Using host floats (but it's fine, these operations do not have guaranteed precision). let f_host = f.to_host(); @@ -879,7 +892,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "atan2" | "fdim" => { - let [f1, f2] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; let f1 = this.read_scalar(f1)?.to_f64()?; let f2 = this.read_scalar(f2)?.to_f64()?; // underscore case for windows, here and below @@ -908,7 +921,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { | "ldexp" | "scalbn" => { - let [x, exp] = this.check_shim(abi, CanonAbi::C , link_name, args)?; + let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?; // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same. let x = this.read_scalar(x)?.to_f64()?; let exp = this.read_scalar(exp)?.to_i32()?; @@ -918,7 +931,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgammaf_r" => { - let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f32()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; @@ -934,7 +947,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "lgamma_r" => { - let [x, signp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let x = this.read_scalar(x)?.to_f64()?; let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?; @@ -952,7 +965,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // LLVM intrinsics "llvm.prefetch" => { - let [p, rw, loc, ty] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [p, rw, loc, ty] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let _ = this.read_pointer(p)?; let rw = this.read_scalar(rw)?.to_i32()?; @@ -979,7 +993,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm // `{i,u}8x16_popcnt` functions. name if name.starts_with("llvm.ctpop.v") => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, op_len) = this.project_to_simd(op)?; let (dest, dest_len) = this.project_to_simd(dest)?; @@ -1015,7 +1029,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { } // FIXME: Move this to an `arm` submodule. "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => { - let [arg] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let arg = this.read_scalar(arg)?.to_i32()?; // Note that different arguments might have different target feature requirements. match arg { diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index 2a7709829ee6..7f594d4fdd6b 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -18,6 +18,7 @@ pub mod global_ctor; pub mod io_error; pub mod os_str; pub mod panic; +pub mod sig; pub mod time; pub mod tls; pub mod unwind; diff --git a/src/tools/miri/src/shims/sig.rs b/src/tools/miri/src/shims/sig.rs new file mode 100644 index 000000000000..8dd08055c7ea --- /dev/null +++ b/src/tools/miri/src/shims/sig.rs @@ -0,0 +1,265 @@ +//! Everything related to checking the signature of shim invocations. + +use rustc_abi::{CanonAbi, ExternAbi}; +use rustc_hir::Safety; +use rustc_middle::ty::{Binder, FnSig, Ty}; +use rustc_span::Symbol; +use rustc_target::callconv::FnAbi; + +use crate::*; + +/// Describes the expected signature of a shim. +pub struct ShimSig<'tcx, const ARGS: usize> { + pub abi: ExternAbi, + pub args: [Ty<'tcx>; ARGS], + pub ret: Ty<'tcx>, +} + +/// Construct a `ShimSig` with convenient syntax: +/// ```rust,ignore +/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize) +/// ``` +#[macro_export] +macro_rules! shim_sig { + (extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => { + |this| $crate::shims::sig::ShimSig { + abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"), + args: [$(shim_sig_arg!(this, $arg)),*], + ret: shim_sig_arg!(this, $ret), + } + }; +} + +/// Helper for `shim_sig!`. +#[macro_export] +macro_rules! shim_sig_arg { + // Unfortuantely we cannot take apart a `ty`-typed token at compile time, + // so we have to stringify it and match at runtime. + ($this:ident, $x:ty) => {{ + match stringify!($x) { + "i8" => $this.tcx.types.i8, + "i16" => $this.tcx.types.i16, + "i32" => $this.tcx.types.i32, + "i64" => $this.tcx.types.i64, + "i128" => $this.tcx.types.i128, + "isize" => $this.tcx.types.isize, + "u8" => $this.tcx.types.u8, + "u16" => $this.tcx.types.u16, + "u32" => $this.tcx.types.u32, + "u64" => $this.tcx.types.u64, + "u128" => $this.tcx.types.u128, + "usize" => $this.tcx.types.usize, + "()" => $this.tcx.types.unit, + "*const _" => $this.machine.layouts.const_raw_ptr.ty, + "*mut _" => $this.machine.layouts.mut_raw_ptr.ty, + ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty, + ty => panic!("unsupported signature type {ty:?}"), + } + }}; +} + +/// Helper function to compare two ABIs. +fn check_shim_abi<'tcx>( + this: &MiriInterpCx<'tcx>, + callee_abi: &FnAbi<'tcx, Ty<'tcx>>, + caller_abi: &FnAbi<'tcx, Ty<'tcx>>, +) -> InterpResult<'tcx> { + if callee_abi.conv != caller_abi.conv { + throw_ub_format!( + r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#, + callee = callee_abi.conv, + caller = caller_abi.conv, + ); + } + if callee_abi.can_unwind && !caller_abi.can_unwind { + throw_ub_format!( + "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding", + ); + } + if caller_abi.c_variadic && !callee_abi.c_variadic { + throw_ub_format!( + "ABI mismatch: calling a non-variadic function with a variadic caller-side signature" + ); + } + if !caller_abi.c_variadic && callee_abi.c_variadic { + throw_ub_format!( + "ABI mismatch: calling a variadic function with a non-variadic caller-side signature" + ); + } + + if callee_abi.fixed_count != caller_abi.fixed_count { + throw_ub_format!( + "ABI mismatch: expected {} arguments, found {} arguments ", + callee_abi.fixed_count, + caller_abi.fixed_count + ); + } + + if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? { + throw_ub!(AbiMismatchReturn { + caller_ty: caller_abi.ret.layout.ty, + callee_ty: callee_abi.ret.layout.ty + }); + } + + for (idx, (caller_arg, callee_arg)) in + caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate() + { + if !this.check_argument_compat(caller_arg, callee_arg)? { + throw_ub!(AbiMismatchArgument { + caller_ty: caller_abi.args[idx].layout.ty, + callee_ty: callee_abi.args[idx].layout.ty + }); + } + } + + interp_ok(()) +} + +fn check_shim_symbol_clash<'tcx>( + this: &mut MiriInterpCx<'tcx>, + link_name: Symbol, +) -> InterpResult<'tcx, ()> { + if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? { + // If compiler-builtins is providing the symbol, then don't treat it as a clash. + // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased + // performance. Note that this means we won't catch any undefined behavior in + // compiler-builtins when running other crates, but Miri can still be run on + // compiler-builtins itself (or any crate that uses it as a normal dependency) + if this.tcx.is_compiler_builtins(instance.def_id().krate) { + return interp_ok(()); + } + + throw_machine_stop!(TerminationInfo::SymbolShimClashing { + link_name, + span: body.span.data(), + }) + } + interp_ok(()) +} + +impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} +pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn check_shim_sig_lenient<'a, const N: usize>( + &mut self, + abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: CanonAbi, + link_name: Symbol, + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + let this = self.eval_context_mut(); + check_shim_symbol_clash(this, link_name)?; + + if abi.conv != exp_abi { + throw_ub_format!( + r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, + abi.conv + ); + } + if abi.c_variadic { + throw_ub_format!( + "calling a non-variadic function with a variadic caller-side signature" + ); + } + + if let Ok(ops) = args.try_into() { + return interp_ok(ops); + } + throw_ub_format!( + "incorrect number of arguments for `{link_name}`: got {}, expected {}", + args.len(), + N + ) + } + + /// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and + /// then returns the list of arguments. + fn check_shim_sig<'a, const N: usize>( + &mut self, + shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>, + link_name: Symbol, + caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>, + caller_args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + let this = self.eval_context_mut(); + let shim_sig = shim_sig(this); + + // Compute full callee ABI. + let mut inputs_and_output = Vec::with_capacity(N.strict_add(1)); + inputs_and_output.extend(&shim_sig.args); + inputs_and_output.push(shim_sig.ret); + let fn_sig_binder = Binder::dummy(FnSig { + inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output), + c_variadic: false, + // This does not matter for the ABI. + safety: Safety::Safe, + abi: shim_sig.abi, + }); + let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?; + + // Check everything. + check_shim_abi(this, callee_fn_abi, caller_fn_abi)?; + check_shim_symbol_clash(this, link_name)?; + + // Return arguments. + if let Ok(ops) = caller_args.try_into() { + return interp_ok(ops); + } + unreachable!() + } + + /// Check shim for variadic function. + /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs. + fn check_shim_sig_variadic_lenient<'a, const N: usize>( + &mut self, + abi: &FnAbi<'tcx, Ty<'tcx>>, + exp_abi: CanonAbi, + link_name: Symbol, + args: &'a [OpTy<'tcx>], + ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> + where + &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>, + { + let this = self.eval_context_mut(); + check_shim_symbol_clash(this, link_name)?; + + if abi.conv != exp_abi { + throw_ub_format!( + r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#, + abi.conv + ); + } + if !abi.c_variadic { + throw_ub_format!( + "calling a variadic function with a non-variadic caller-side signature" + ); + } + if abi.fixed_count != u32::try_from(N).unwrap() { + throw_ub_format!( + "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}", + link_name.as_str(), + abi.fixed_count + ) + } + if let Some(args) = args.split_first_chunk() { + return interp_ok(args); + } + panic!("mismatch between signature and `args` slice"); + } +} + +/// Check that the number of varargs is at least the minimum what we expect. +/// Fixed args should not be included. +pub fn check_min_vararg_count<'a, 'tcx, const N: usize>( + name: &'a str, + args: &'a [OpTy<'tcx>], +) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> { + if let Some((ops, _)) = args.split_first_chunk() { + return interp_ok(ops); + } + throw_ub_format!( + "not enough variadic arguments for `{name}`: got {}, expected at least {}", + args.len(), + N + ) +} diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index 690b5295681d..04c5d28838bb 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -26,29 +26,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Miscellaneous "__errno" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs index 5d17d6c8517b..4e7b21d7d944 100644 --- a/src/tools/miri/src/shims/unix/android/thread.rs +++ b/src/tools/miri/src/shims/unix/android/thread.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult}; use crate::*; @@ -16,7 +16,7 @@ pub fn prctl<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch. let pr_set_name = 15; diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index b420955501c8..e226a55d8b1e 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -7,8 +7,8 @@ use std::io::ErrorKind; use rand::Rng; use rustc_abi::Size; -use crate::helpers::check_min_vararg_count; use crate::shims::files::FileDescription; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::linux_like::epoll::EpollReadyEvents; use crate::shims::unix::*; use crate::*; diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 548eabb1b9fd..55906f4eb954 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -1,7 +1,7 @@ use std::ffi::OsStr; use std::str; -use rustc_abi::{CanonAbi, ExternAbi, Size}; +use rustc_abi::{CanonAbi, Size}; use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; @@ -14,7 +14,7 @@ use self::shims::unix::solarish::foreign_items as solarish; use crate::concurrency::cpu_affinity::CpuAffinityMask; use crate::shims::alloc::EvalContextExt as _; use crate::shims::unix::*; -use crate::*; +use crate::{shim_sig, *}; pub fn is_dyn_sym(name: &str, target_os: &str) -> bool { match name { @@ -111,40 +111,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment related shims "getenv" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.getenv(name)?; this.write_pointer(result, dest)?; } "unsetenv" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.unsetenv(name)?; this.write_scalar(result, dest)?; } "setenv" => { - let [name, value, overwrite] = this.check_shim_abi( + let [name, value, overwrite] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.machine.layouts.const_raw_ptr.ty, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.i32, - ], - this.tcx.types.i32, args, )?; this.read_scalar(overwrite)?.to_i32()?; @@ -152,48 +142,40 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } "getcwd" => { - let [buf, size] = this.check_shim_abi( + let [buf, size] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, usize) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.getcwd(buf, size)?; this.write_pointer(result, dest)?; } "chdir" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.chdir(path)?; this.write_scalar(result, dest)?; } "getpid" => { - let [] = this.check_shim_abi( + let [] = this.check_shim_sig( + shim_sig!(extern "C" fn() -> libc::pid_t), link_name, abi, - ExternAbi::C { unwind: false }, - [], - this.libc_ty_layout("pid_t").ty, args, )?; let result = this.getpid()?; this.write_scalar(result, dest)?; } "sysconf" => { - let [val] = this.check_shim_abi( + let [val] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.isize, args, )?; let result = this.sysconf(val)?; @@ -201,12 +183,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // File descriptors "read" => { - let [fd, buf, count] = this.check_shim_abi( + let [fd, buf, count] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.usize], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -215,16 +195,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, None, dest)?; } "write" => { - let [fd, buf, n] = this.check_shim_abi( + let [fd, buf, n] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -234,98 +208,64 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write(fd, buf, count, None, dest)?; } "pread" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, buf, count, offset] = this.check_shim_abi( + let [fd, buf, count, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - off_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, buf, n, offset] = this.check_shim_abi( + let [fd, buf, n, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - off_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } "pread64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, buf, count, offset] = this.check_shim_abi( + let [fd, buf, count, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - off64_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(count)?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; this.read(fd, buf, count, Some(offset), dest)?; } "pwrite64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, buf, n, offset] = this.check_shim_abi( + let [fd, buf, n, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.machine.layouts.const_raw_ptr.ty, - this.tcx.types.usize, - off64_t.ty, - ], - this.tcx.types.isize, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; let buf = this.read_pointer(buf)?; let count = this.read_target_usize(n)?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } "close" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.close(fd)?; @@ -333,17 +273,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "fcntl" => { let ([fd_num, cmd], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.fcntl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } "dup" => { - let [old_fd] = this.check_shim_abi( + let [old_fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; @@ -351,12 +289,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(new_fd, dest)?; } "dup2" => { - let [old_fd, new_fd] = this.check_shim_abi( + let [old_fd, new_fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let old_fd = this.read_scalar(old_fd)?.to_i32()?; @@ -367,12 +303,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "flock" => { // Currently this function does not exist on all Unixes, e.g. on Solaris. this.check_target_os(&["linux", "freebsd", "macos", "illumos"], link_name)?; - let [fd, op] = this.check_shim_abi( + let [fd, op] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; @@ -386,230 +320,187 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `open` is variadic, the third argument is only present when the second argument // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set let ([path_raw, flag], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.open(path_raw, flag, varargs)?; this.write_scalar(result, dest)?; } "unlink" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.unlink(path)?; this.write_scalar(result, dest)?; } "symlink" => { - let [target, linkpath] = this.check_shim_abi( + let [target, linkpath] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.symlink(target, linkpath)?; this.write_scalar(result, dest)?; } "rename" => { - let [oldpath, newpath] = this.check_shim_abi( + let [oldpath, newpath] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.rename(oldpath, newpath)?; this.write_scalar(result, dest)?; } "mkdir" => { - let [path, mode] = this.check_shim_abi( + let [path, mode] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, libc::mode_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.libc_ty_layout("mode_t").ty], - this.tcx.types.i32, args, )?; let result = this.mkdir(path, mode)?; this.write_scalar(result, dest)?; } "rmdir" => { - let [path] = this.check_shim_abi( + let [path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.rmdir(path)?; this.write_scalar(result, dest)?; } "opendir" => { - let [name] = this.check_shim_abi( + let [name] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "closedir" => { - let [dirp] = this.check_shim_abi( + let [dirp] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.closedir(dirp)?; this.write_scalar(result, dest)?; } "lseek64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, offset, whence] = this.check_shim_abi( + let [fd, offset, whence] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off64_t.ty, this.tcx.types.i32], - off64_t.ty, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_scalar(offset)?.to_int(off64_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; let whence = this.read_scalar(whence)?.to_i32()?; this.lseek64(fd, offset, whence, dest)?; } "lseek" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, offset, whence] = this.check_shim_abi( + let [fd, offset, whence] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty, this.tcx.types.i32], - off_t.ty, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let offset = this.read_scalar(offset)?.to_int(off_t.size)?; + let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?; let whence = this.read_scalar(whence)?.to_i32()?; this.lseek64(fd, offset, whence, dest)?; } "ftruncate64" => { - let off64_t = this.libc_ty_layout("off64_t"); - let [fd, length] = this.check_shim_abi( + let [fd, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off64_t.ty], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let length = this.read_scalar(length)?.to_int(off64_t.size)?; + let length = this.read_scalar(length)?.to_int(length.layout.size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } "ftruncate" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, length] = this.check_shim_abi( + let [fd, length] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty], - this.tcx.types.i32, args, )?; let fd = this.read_scalar(fd)?.to_i32()?; - let length = this.read_scalar(length)?.to_int(off_t.size)?; + let length = this.read_scalar(length)?.to_int(length.layout.size)?; let result = this.ftruncate64(fd, length)?; this.write_scalar(result, dest)?; } "fsync" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.fsync(fd)?; this.write_scalar(result, dest)?; } "fdatasync" => { - let [fd] = this.check_shim_abi( + let [fd] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.fdatasync(fd)?; this.write_scalar(result, dest)?; } "readlink" => { - let [pathname, buf, bufsize] = this.check_shim_abi( + let [pathname, buf, bufsize] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _, usize) -> isize), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.machine.layouts.const_raw_ptr.ty, - this.machine.layouts.mut_raw_ptr.ty, - this.tcx.types.usize, - ], - this.tcx.types.isize, args, )?; let result = this.readlink(pathname, buf, bufsize)?; this.write_scalar(Scalar::from_target_isize(result, this), dest)?; } "posix_fadvise" => { - let off_t = this.libc_ty_layout("off_t"); - let [fd, offset, len, advice] = this.check_shim_abi( + let [fd, offset, len, advice] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, libc::off_t, libc::off_t, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.tcx.types.i32, off_t.ty, off_t.ty, this.tcx.types.i32], - this.tcx.types.i32, args, )?; this.read_scalar(fd)?.to_i32()?; - this.read_scalar(offset)?.to_int(off_t.size)?; - this.read_scalar(len)?.to_int(off_t.size)?; + this.read_scalar(offset)?.to_int(offset.layout.size)?; + this.read_scalar(len)?.to_int(len.layout.size)?; this.read_scalar(advice)?.to_i32()?; // fadvise is only informational, we can ignore it. this.write_null(dest)?; } "realpath" => { - let [path, resolved_path] = this.check_shim_abi( + let [path, resolved_path] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } "mkstemp" => { - let [template] = this.check_shim_abi( + let [template] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.mkstemp(template)?; @@ -618,29 +509,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Unnamed sockets and pipes "socketpair" => { - let [domain, type_, protocol, sv] = this.check_shim_abi( + let [domain, type_, protocol, sv] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, i32, i32, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [ - this.tcx.types.i32, - this.tcx.types.i32, - this.tcx.types.i32, - this.machine.layouts.mut_raw_ptr.ty, - ], - this.tcx.types.i32, args, )?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } "pipe" => { - let [pipefd] = this.check_shim_abi( + let [pipefd] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.pipe2(pipefd, /*flags*/ None)?; @@ -649,12 +531,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "pipe2" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "solaris", "illumos"], link_name)?; - let [pipefd, flags] = this.check_shim_abi( + let [pipefd, flags] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, i32) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.tcx.types.i32], - this.tcx.types.i32, args, )?; let result = this.pipe2(pipefd, Some(flags))?; @@ -663,36 +543,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time "gettimeofday" => { - let [tv, tz] = this.check_shim_abi( + let [tv, tz] = this.check_shim_sig( + shim_sig!(extern "C" fn(*mut _, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.mut_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; let result = this.gettimeofday(tv, tz)?; this.write_scalar(result, dest)?; } "localtime_r" => { - let [timep, result_op] = this.check_shim_abi( + let [timep, result_op] = this.check_shim_sig( + shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _), link_name, abi, - ExternAbi::C { unwind: false }, - [this.machine.layouts.const_raw_ptr.ty, this.machine.layouts.mut_raw_ptr.ty], - this.machine.layouts.mut_raw_ptr.ty, args, )?; let result = this.localtime_r(timep, result_op)?; this.write_pointer(result, dest)?; } "clock_gettime" => { - let [clk_id, tp] = this.check_shim_abi( + let [clk_id, tp] = this.check_shim_sig( + shim_sig!(extern "C" fn(libc::clockid_t, *mut _) -> i32), link_name, abi, - ExternAbi::C { unwind: false }, - [this.libc_ty_layout("clockid_t").ty, this.machine.layouts.mut_raw_ptr.ty], - this.tcx.types.i32, args, )?; this.clock_gettime(clk_id, tp, dest)?; @@ -700,20 +574,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "posix_memalign" => { - let [memptr, align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [memptr, align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } "mmap" => { let [addr, length, prot, flags, fd, offset] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?; let ptr = this.mmap(addr, length, prot, flags, fd, offset)?; this.write_scalar(ptr, dest)?; } "munmap" => { - let [addr, length] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [addr, length] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.munmap(addr, length)?; this.write_scalar(result, dest)?; } @@ -721,7 +597,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "reallocarray" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [ptr, nmemb, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, nmemb, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let nmemb = this.read_target_usize(nmemb)?; let size = this.read_target_usize(size)?; @@ -744,14 +621,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "aligned_alloc" => { // This is a C11 function, we assume all Unixes have it. // (MSVC explicitly does not support this.) - let [align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } // Dynamic symbol loading "dlsym" => { - let [handle, symbol] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [handle, symbol] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(handle)?; let symbol = this.read_pointer(symbol)?; let name = this.read_c_str(symbol)?; @@ -767,7 +646,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "pthread_key_create" => { - let [key, dtor] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key, dtor] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?; let dtor = this.read_pointer(dtor)?; @@ -795,21 +674,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "pthread_key_delete" => { - let [key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; this.machine.tls.delete_tls_key(key)?; // Return success (0) this.write_null(dest)?; } "pthread_getspecific" => { - let [key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "pthread_setspecific" => { - let [key, new_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [key, new_ptr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let key = this.read_scalar(key)?.to_bits(key.layout.size)?; let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -821,117 +701,124 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "pthread_mutexattr_init" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutexattr_init(attr)?; this.write_null(dest)?; } "pthread_mutexattr_settype" => { - let [attr, kind] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, kind] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutexattr_settype(attr, kind)?; this.write_scalar(result, dest)?; } "pthread_mutexattr_destroy" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutexattr_destroy(attr)?; this.write_null(dest)?; } "pthread_mutex_init" => { - let [mutex, attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex, attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_init(mutex, attr)?; this.write_null(dest)?; } "pthread_mutex_lock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_lock(mutex, dest)?; } "pthread_mutex_trylock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutex_trylock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_unlock" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_mutex_unlock(mutex)?; this.write_scalar(result, dest)?; } "pthread_mutex_destroy" => { - let [mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [mutex] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_mutex_destroy(mutex)?; this.write_int(0, dest)?; } "pthread_rwlock_rdlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_rdlock(rwlock, dest)?; } "pthread_rwlock_tryrdlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_rwlock_tryrdlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_wrlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_wrlock(rwlock, dest)?; } "pthread_rwlock_trywrlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_rwlock_trywrlock(rwlock)?; this.write_scalar(result, dest)?; } "pthread_rwlock_unlock" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_unlock(rwlock)?; this.write_null(dest)?; } "pthread_rwlock_destroy" => { - let [rwlock] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [rwlock] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_rwlock_destroy(rwlock)?; this.write_null(dest)?; } "pthread_condattr_init" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_init(attr)?; this.write_null(dest)?; } "pthread_condattr_setclock" => { - let [attr, clock_id] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.pthread_condattr_setclock(attr, clock_id)?; this.write_scalar(result, dest)?; } "pthread_condattr_getclock" => { - let [attr, clock_id] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr, clock_id] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_getclock(attr, clock_id)?; this.write_null(dest)?; } "pthread_condattr_destroy" => { - let [attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [attr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_condattr_destroy(attr)?; this.write_null(dest)?; } "pthread_cond_init" => { - let [cond, attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_init(cond, attr)?; this.write_null(dest)?; } "pthread_cond_signal" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_signal(cond)?; this.write_null(dest)?; } "pthread_cond_broadcast" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_broadcast(cond)?; this.write_null(dest)?; } "pthread_cond_wait" => { - let [cond, mutex] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_wait(cond, mutex, dest)?; } "pthread_cond_timedwait" => { - let [cond, mutex, abstime] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond, mutex, abstime] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_timedwait(cond, mutex, abstime, dest)?; } "pthread_cond_destroy" => { - let [cond] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cond] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_cond_destroy(cond)?; this.write_null(dest)?; } @@ -939,31 +826,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_create" => { let [thread, attr, start, arg] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_create(thread, attr, start, arg)?; this.write_null(dest)?; } "pthread_join" => { - let [thread, retval] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, retval] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.pthread_join(thread, retval, dest)?; } "pthread_detach" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.pthread_detach(thread)?; this.write_scalar(res, dest)?; } "pthread_self" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.pthread_self()?; this.write_scalar(res, dest)?; } "sched_yield" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.sched_yield()?; this.write_null(dest)?; } "nanosleep" => { - let [duration, rem] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [duration, rem] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.nanosleep(duration, rem)?; this.write_scalar(result, dest)?; } @@ -974,14 +863,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; let [clock_id, flags, req, rem] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.clock_nanosleep(clock_id, flags, req, rem)?; this.write_scalar(result, dest)?; } "sched_getaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [pid, cpusetsize, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1018,7 +908,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "sched_setaffinity" => { // Currently this function does not exist on all Unixes, e.g. on macOS. this.check_target_os(&["linux", "freebsd", "android"], link_name)?; - let [pid, cpusetsize, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pid, cpusetsize, mask] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let pid = this.read_scalar(pid)?.to_u32()?; let cpusetsize = this.read_target_usize(cpusetsize)?; let mask = this.read_pointer(mask)?; @@ -1058,13 +949,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "isatty" => { - let [fd] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.isatty(fd)?; this.write_scalar(result, dest)?; } "pthread_atfork" => { let [prepare, parent, child] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_pointer(prepare)?; this.read_pointer(parent)?; this.read_pointer(child)?; @@ -1078,7 +969,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &["linux", "macos", "freebsd", "illumos", "solaris", "android"], link_name, )?; - let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [buf, bufsize] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let buf = this.read_pointer(buf)?; let bufsize = this.read_target_usize(bufsize)?; @@ -1096,7 +988,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "strerror_r" => { - let [errnum, buf, buflen] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [errnum, buf, buflen] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } @@ -1108,7 +1001,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &["linux", "freebsd", "illumos", "solaris", "android"], link_name, )?; - let [ptr, len, flags] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, len, flags] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; let _flags = this.read_scalar(flags)?.to_i32()?; @@ -1120,7 +1014,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This function is non-standard but exists with the same signature and // same behavior (eg never fails) on FreeBSD and Solaris/Illumos. this.check_target_os(&["freebsd", "illumos", "solaris"], link_name)?; - let [ptr, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -1144,12 +1038,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name, )?; // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } "getuid" | "geteuid" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // For now, just pretend we always have this fixed UID. this.write_int(UID, dest)?; } @@ -1157,7 +1051,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_attr_getguardsize" if this.frame_in_std() => { - let [_attr, guard_size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_attr, guard_size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let guard_size_layout = this.machine.layouts.usize; let guard_size = this.deref_pointer_as(guard_size, guard_size_layout)?; this.write_scalar( @@ -1170,11 +1065,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pthread_attr_init" | "pthread_attr_destroy" if this.frame_in_std() => { - let [_] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "pthread_attr_setstacksize" if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } @@ -1182,7 +1077,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here. // Hence we can mostly ignore the input `attr_place`. let [attr_place, addr_place, size_place] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?; let addr_place = this.deref_pointer_as(addr_place, this.machine.layouts.usize)?; @@ -1202,18 +1097,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "signal" | "sigaltstack" if this.frame_in_std() => { - let [_, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "sigaction" | "mprotect" if this.frame_in_std() => { - let [_, _, _] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, _, _] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } "getpwuid_r" | "__posix_getpwuid_r" if this.frame_in_std() => { // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish let [uid, pwd, buf, buflen, result] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`getpwuid_r`")?; let uid = this.read_scalar(uid)?.to_u32()?; diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index 33564a2f84c7..9e247053fbcd 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -24,7 +24,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let max_len = u64::MAX; // FreeBSD does not seem to have a limit. let res = match this.pthread_setname_np( this.read_scalar(thread)?, @@ -39,7 +40,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // FreeBSD's pthread_getname_np uses strlcpy, which truncates the resulting value, // but always adds a null terminator (except for zero-sized buffers). // https://github.com/freebsd/freebsd-src/blob/c2d93a803acef634bd0eede6673aeea59e90c277/lib/libthr/thread/thr_info.c#L119-L144 @@ -57,7 +59,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getthreadid_np" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -65,7 +67,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "cpuset_getaffinity" => { // The "same" kind of api as `sched_getaffinity` but more fine grained control for FreeBSD specifically. let [level, which, id, set_size, mask] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let level = this.read_scalar(level)?.to_i32()?; let which = this.read_scalar(which)?.to_i32()?; @@ -129,7 +131,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "_umtx_op" => { let [obj, op, val, uaddr, uaddr2] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this._umtx_op(obj, op, val, uaddr, uaddr2, dest)?; } @@ -137,29 +139,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For those, we both intercept `func` and `call@FBSD_1.0` symbols cases // since freebsd 12 the former form can be expected. "stat" | "stat@FBSD_1.0" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat@FBSD_1.0" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat@FBSD_1.0" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r@FBSD_1.0" => { - let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp, entry, result] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } // Miscellaneous "__error" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } @@ -167,7 +170,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_attr_get_np" if this.frame_in_std() => { - let [_thread, _attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_thread, _attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0f2878ad26c8..f9bcacf64c41 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -13,9 +13,9 @@ use rustc_abi::Size; use rustc_data_structures::fx::FxHashMap; use self::shims::time::system_time_to_duration; -use crate::helpers::check_min_vararg_count; use crate::shims::files::FileHandle; use crate::shims::os_str::bytes_to_os_str; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::fd::{FlockOp, UnixFileDescription}; use crate::*; diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index b3e99e6cc68f..e7e0c3b6ecd9 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -37,48 +37,50 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // File related shims "readdir64" => { - let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_solarish_readdir64("dirent64", dirp)?; this.write_scalar(result, dest)?; } "sync_file_range" => { let [fd, offset, nbytes, flags] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.sync_file_range(fd, offset, nbytes, flags)?; this.write_scalar(result, dest)?; } "statx" => { let [dirfd, pathname, flags, mask, statxbuf] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?; this.write_scalar(result, dest)?; } // epoll, eventfd "epoll_create1" => { - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = match this.pthread_setname_np( this.read_scalar(thread)?, this.read_scalar(name)?, @@ -93,7 +95,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of glibc, the length of the output buffer must // be not shorter than TASK_COMM_LEN. @@ -116,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "gettid" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.unix_gettid(link_name.as_str())?; this.write_scalar(result, dest)?; } @@ -129,34 +132,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "mmap64" => { let [addr, length, prot, flags, fd, offset] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let offset = this.read_scalar(offset)?.to_i64()?; let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?; this.write_scalar(ptr, dest)?; } "mremap" => { let ([old_address, old_size, new_size, flags], _) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let ptr = this.mremap(old_address, old_size, new_size, flags)?; this.write_scalar(ptr, dest)?; } "__xpg_strerror_r" => { - let [errnum, buf, buflen] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [errnum, buf, buflen] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.strerror_r(errnum, buf, buflen)?; this.write_scalar(result, dest)?; } "__errno_location" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "__libc_current_sigrtmin" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMIN, dest)?; } "__libc_current_sigrtmax" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_int(SIGRTMAX, dest)?; } @@ -164,7 +168,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "pthread_getattr_np" if this.frame_in_std() => { - let [_thread, _attr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_thread, _attr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_null(dest)?; } diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 9fad74c0241e..5f032c52deeb 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -1,5 +1,5 @@ use crate::concurrency::sync::FutexRef; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::*; struct LinuxFutex { diff --git a/src/tools/miri/src/shims/unix/linux_like/syscall.rs b/src/tools/miri/src/shims/unix/linux_like/syscall.rs index d3534e6e1bc6..106e6c448d06 100644 --- a/src/tools/miri/src/shims/unix/linux_like/syscall.rs +++ b/src/tools/miri/src/shims/unix/linux_like/syscall.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use crate::helpers::check_min_vararg_count; +use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::env::EvalContextExt; use crate::shims::unix::linux_like::eventfd::EvalContextExt as _; use crate::shims::unix::linux_like::sync::futex; @@ -16,7 +16,7 @@ pub fn syscall<'tcx>( args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - let ([op], varargs) = ecx.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + let ([op], varargs) = ecx.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; // The syscall variadic function is legal to call with more arguments than needed, // extra arguments are simply ignored. The important check is that when we use an // argument, we have to also check all arguments *before* it to ensure that they diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 233037180910..297d903c6ba4 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -35,64 +35,67 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // errno "__error" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } // File related shims "close$NOCANCEL" => { - let [result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [result] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.close(result)?; this.write_scalar(result, dest)?; } "stat" | "stat64" | "stat$INODE64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" | "lstat$INODE64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" | "fstat$INODE64" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "opendir$INODE64" => { - let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.opendir(name)?; this.write_scalar(result, dest)?; } "readdir_r" | "readdir_r$INODE64" => { - let [dirp, entry, result] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp, entry, result] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_readdir_r(dirp, entry, result)?; this.write_scalar(result, dest)?; } "realpath$DARWIN_EXTSN" => { - let [path, resolved_path] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, resolved_path] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.realpath(path, resolved_path)?; this.write_scalar(result, dest)?; } "ioctl" => { let ([fd_num, cmd], varargs) = - this.check_shim_variadic(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.ioctl(fd_num, cmd, varargs)?; this.write_scalar(result, dest)?; } // Environment related shims "_NSGetEnviron" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let environ = this.machine.env_vars.unix().environ(); this.write_pointer(environ, dest)?; } // Random data generation "CCRandomGenerateBytes" => { - let [bytes, count] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [bytes, count] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let bytes = this.read_pointer(bytes)?; let count = this.read_target_usize(count)?; let success = this.eval_libc_i32("kCCSuccess"); @@ -102,28 +105,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "mach_absolute_time" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.mach_absolute_time()?; this.write_scalar(result, dest)?; } "mach_timebase_info" => { - let [info] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [info] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.mach_timebase_info(info)?; this.write_scalar(result, dest)?; } // Access to command-line arguments "_NSGetArgc" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?; } "_NSGetArgv" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?; } "_NSGetExecutablePath" => { - let [buf, bufsize] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [buf, bufsize] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.check_no_isolation("`_NSGetExecutablePath`")?; let buf_ptr = this.read_pointer(buf)?; @@ -148,7 +152,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Thread-local storage "_tlv_atexit" => { - let [dtor, data] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dtor, data] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let dtor = this.read_pointer(dtor)?; let dtor = this.get_ptr_fn(dtor)?.as_instance()?; let data = this.read_scalar(data)?; @@ -158,13 +163,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "pthread_get_stackaddr_np" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size()); this.write_scalar(stack_addr, dest)?; } "pthread_get_stacksize_np" => { - let [thread] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.read_target_usize(thread)?; let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size()); this.write_scalar(stack_size, dest)?; @@ -172,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "pthread_setname_np" => { - let [name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [name] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The real implementation has logic in two places: // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200, @@ -199,7 +204,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // The function's behavior isn't portable between platforms. // In case of macOS, a truncated name (due to a too small buffer) @@ -223,7 +229,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_threadid_np" => { - let [thread, tid_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, tid_ptr] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.apple_pthread_threadip_np(thread, tid_ptr)?; this.write_scalar(res, dest)?; } @@ -231,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "os_sync_wait_on_address" => { let [addr_op, value_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -243,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_deadline" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -255,7 +262,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wait_on_address_with_timeout" => { let [addr_op, value_op, size_op, flags_op, clock_op, timeout_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wait_on_address( addr_op, value_op, @@ -267,36 +274,36 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "os_sync_wake_by_address_any" => { let [addr_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ false, dest, )?; } "os_sync_wake_by_address_all" => { let [addr_op, size_op, flags_op] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_sync_wake_by_address( addr_op, size_op, flags_op, /* all */ true, dest, )?; } "os_unfair_lock_lock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_lock(lock_op)?; } "os_unfair_lock_trylock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_trylock(lock_op, dest)?; } "os_unfair_lock_unlock" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_unlock(lock_op)?; } "os_unfair_lock_assert_owner" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_owner(lock_op)?; } "os_unfair_lock_assert_not_owner" => { - let [lock_op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [lock_op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.os_unfair_lock_assert_not_owner(lock_op)?; } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index e3d15b89be6d..d7033a65fe22 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -27,32 +27,34 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // epoll, eventfd (NOT available on Solaris!) "epoll_create1" => { this.assert_target_os("illumos", "epoll_create1"); - let [flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_create1(flag)?; this.write_scalar(result, dest)?; } "epoll_ctl" => { this.assert_target_os("illumos", "epoll_ctl"); - let [epfd, op, fd, event] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [epfd, op, fd, event] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.epoll_ctl(epfd, op, fd, event)?; this.write_scalar(result, dest)?; } "epoll_wait" => { this.assert_target_os("illumos", "epoll_wait"); let [epfd, events, maxevents, timeout] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.epoll_wait(epfd, events, maxevents, timeout, dest)?; } "eventfd" => { this.assert_target_os("illumos", "eventfd"); - let [val, flag] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val, flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.eventfd(val, flag)?; this.write_scalar(result, dest)?; } // Threading "pthread_setname_np" => { - let [thread, name] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // THREAD_NAME_MAX allows a thread name of 31+1 length // https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613 let max_len = 32; @@ -70,7 +72,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "pthread_getname_np" => { - let [thread, name, len] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [thread, name, len] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // See https://illumos.org/man/3C/pthread_getname_np for the error codes. let res = match this.pthread_getname_np( this.read_scalar(thread)?, @@ -87,22 +90,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // File related shims "stat" | "stat64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_stat(path, buf)?; this.write_scalar(result, dest)?; } "lstat" | "lstat64" => { - let [path, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_lstat(path, buf)?; this.write_scalar(result, dest)?; } "fstat" | "fstat64" => { - let [fd, buf] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.macos_fbsd_solarish_fstat(fd, buf)?; this.write_scalar(result, dest)?; } "readdir" => { - let [dirp] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.linux_solarish_readdir64("dirent", dirp)?; this.write_scalar(result, dest)?; } @@ -110,20 +113,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Sockets and pipes "__xnet_socketpair" => { let [domain, type_, protocol, sv] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.socketpair(domain, type_, protocol, sv)?; this.write_scalar(result, dest)?; } // Miscellaneous "___errno" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let errno_place = this.last_error_place()?; this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?; } "stack_getbounds" => { - let [stack] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [stack] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?; this.write_int_fields_named( @@ -141,7 +144,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "pset_info" => { - let [pset, tpe, cpus, list] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [pset, tpe, cpus, list] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // We do not need to handle the current process cpu mask, available_parallelism // implementation pass null anyway. We only care for the number of // cpus. @@ -170,7 +174,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "__sysconf_xpg7" => { - let [val] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [val] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.sysconf(val)?; this.write_scalar(result, dest)?; } diff --git a/src/tools/miri/src/shims/unwind.rs b/src/tools/miri/src/shims/unwind.rs index ba0c50b54b47..0dd2b20487d3 100644 --- a/src/tools/miri/src/shims/unwind.rs +++ b/src/tools/miri/src/shims/unwind.rs @@ -16,7 +16,6 @@ use rustc_abi::ExternAbi; use rustc_middle::mir; use rustc_target::spec::PanicStrategy; -use self::helpers::check_intrinsic_arg_count; use crate::*; /// Holds all of the relevant data for when unwinding hits a `try` frame. @@ -60,7 +59,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// Handles the `catch_unwind` intrinsic. fn handle_catch_unwind( &mut self, - args: &[OpTy<'tcx>], + try_fn: &OpTy<'tcx>, + data: &OpTy<'tcx>, + catch_fn: &OpTy<'tcx>, dest: &MPlaceTy<'tcx>, ret: Option, ) -> InterpResult<'tcx> { @@ -78,7 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // a pointer to `Box`. // Get all the arguments. - let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?; let try_fn = this.read_pointer(try_fn)?; let data = this.read_immediate(data)?; let catch_fn = this.read_pointer(catch_fn)?; diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index 8d92d0f3381f..bfcdbd8130d8 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -23,12 +23,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Allocation "posix_memalign" => { - let [memptr, align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [memptr, align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let result = this.posix_memalign(memptr, align, size)?; this.write_scalar(result, dest)?; } "aligned_alloc" => { - let [align, size] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [align, size] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let res = this.aligned_alloc(align, size)?; this.write_pointer(res, dest)?; } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 959abc0bacaa..7b13f1d90807 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -157,42 +157,44 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match link_name.as_str() { // Environment related shims "GetEnvironmentVariableW" => { - let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [name, buf, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetEnvironmentVariableW(name, buf, size)?; this.write_scalar(result, dest)?; } "SetEnvironmentVariableW" => { - let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?; + let [name, value] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.SetEnvironmentVariableW(name, value)?; this.write_scalar(result, dest)?; } "GetEnvironmentStringsW" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetEnvironmentStringsW()?; this.write_pointer(result, dest)?; } "FreeEnvironmentStringsW" => { - let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?; + let [env_block] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.FreeEnvironmentStringsW(env_block)?; this.write_scalar(result, dest)?; } "GetCurrentDirectoryW" => { - let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?; + let [size, buf] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetCurrentDirectoryW(size, buf)?; this.write_scalar(result, dest)?; } "SetCurrentDirectoryW" => { - let [path] = this.check_shim(abi, sys_conv, link_name, args)?; + let [path] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.SetCurrentDirectoryW(path)?; this.write_scalar(result, dest)?; } "GetUserProfileDirectoryW" => { - let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [token, buf, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetUserProfileDirectoryW(token, buf, size)?; this.write_scalar(result, dest)?; } "GetCurrentProcessId" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.GetCurrentProcessId()?; this.write_scalar(result, dest)?; } @@ -209,7 +211,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.NtWriteFile( handle, event, @@ -234,7 +236,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { n, byte_offset, key, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.NtReadFile( handle, event, @@ -250,7 +252,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetFullPathNameW" => { let [filename, size, buffer, filepart] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.check_no_isolation("`GetFullPathNameW`")?; let filename = this.read_pointer(filename)?; @@ -287,7 +289,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { creation_disposition, flags_and_attributes, template_file, - ] = this.check_shim(abi, sys_conv, link_name, args)?; + ] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.CreateFileW( file_name, desired_access, @@ -300,18 +302,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(handle.to_scalar(this), dest)?; } "GetFileInformationByHandle" => { - let [handle, info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.GetFileInformationByHandle(handle, info)?; this.write_scalar(res, dest)?; } "DeleteFileW" => { - let [file_name] = this.check_shim(abi, sys_conv, link_name, args)?; + let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.DeleteFileW(file_name)?; this.write_scalar(res, dest)?; } "SetFilePointerEx" => { let [file, distance_to_move, new_file_pointer, move_method] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?; this.write_scalar(res, dest)?; @@ -319,7 +321,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Allocation "HeapAlloc" => { - let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, flags, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; @@ -341,7 +344,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr, dest)?; } "HeapFree" => { - let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, flags, ptr] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let ptr = this.read_pointer(ptr)?; @@ -354,7 +358,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "HeapReAlloc" => { let [handle, flags, old_ptr, size] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(handle)?; this.read_scalar(flags)?.to_u32()?; let old_ptr = this.read_pointer(old_ptr)?; @@ -374,7 +378,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(new_ptr, dest)?; } "LocalFree" => { - let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL." // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree) @@ -386,17 +390,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // errno "SetLastError" => { - let [error] = this.check_shim(abi, sys_conv, link_name, args)?; + let [error] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let error = this.read_scalar(error)?; this.set_last_error(error)?; } "GetLastError" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let last_error = this.get_last_error()?; this.write_scalar(last_error, dest)?; } "RtlNtStatusToDosError" => { - let [status] = this.check_shim(abi, sys_conv, link_name, args)?; + let [status] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let status = this.read_scalar(status)?.to_u32()?; let err = match status { // STATUS_MEDIA_WRITE_PROTECTED => ERROR_WRITE_PROTECT @@ -418,7 +422,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Querying system information "GetSystemInfo" => { // Also called from `page_size` crate. - let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [system_info] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let system_info = this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?; // Initialize with `0`. @@ -441,19 +445,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This just creates a key; Windows does not natively support TLS destructors. // Create key and return it. - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = this.machine.tls.create_tls_key(None, dest.layout.size)?; this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; } "TlsGetValue" => { - let [key] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let ptr = this.machine.tls.load_tls(key, active_thread, this)?; this.write_scalar(ptr, dest)?; } "TlsSetValue" => { - let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key, new_ptr] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); let active_thread = this.active_thread(); let new_data = this.read_scalar(new_ptr)?; @@ -463,7 +467,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_int(1, dest)?; } "TlsFree" => { - let [key] = this.check_shim(abi, sys_conv, link_name, args)?; + let [key] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let key = u128::from(this.read_scalar(key)?.to_u32()?); this.machine.tls.delete_tls_key(key)?; @@ -473,7 +477,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Access to command-line arguments "GetCommandLineW" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_pointer( this.machine.cmd_line.expect("machine must be initialized"), dest, @@ -483,29 +487,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Time related shims "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => { #[allow(non_snake_case)] - let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?; + let [LPFILETIME] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?; } "QueryPerformanceCounter" => { #[allow(non_snake_case)] - let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?; + let [lpPerformanceCount] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.QueryPerformanceCounter(lpPerformanceCount)?; this.write_scalar(result, dest)?; } "QueryPerformanceFrequency" => { #[allow(non_snake_case)] - let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?; + let [lpFrequency] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.QueryPerformanceFrequency(lpFrequency)?; this.write_scalar(result, dest)?; } "Sleep" => { - let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?; + let [timeout] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.Sleep(timeout)?; } "CreateWaitableTimerExW" => { let [attributes, name, flags, access] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_pointer(attributes)?; this.read_pointer(name)?; this.read_scalar(flags)?.to_u32()?; @@ -519,27 +524,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Synchronization primitives "InitOnceBeginInitialize" => { let [ptr, flags, pending, context] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?; } "InitOnceComplete" => { - let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, flags, context] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let result = this.InitOnceComplete(ptr, flags, context)?; this.write_scalar(result, dest)?; } "WaitOnAddress" => { let [ptr_op, compare_op, size_op, timeout_op] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?; } "WakeByAddressSingle" => { - let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WakeByAddressSingle(ptr_op)?; } "WakeByAddressAll" => { - let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr_op] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WakeByAddressAll(ptr_op)?; } @@ -547,7 +553,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Dynamic symbol loading "GetProcAddress" => { #[allow(non_snake_case)] - let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?; + let [hModule, lpProcName] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(hModule)?; let name = this.read_c_str(this.read_pointer(lpProcName)?)?; if let Ok(name) = str::from_utf8(name) @@ -563,7 +570,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Threading "CreateThread" => { let [security, stacksize, start, arg, flags, thread] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let thread_id = this.CreateThread(security, stacksize, start, arg, flags, thread)?; @@ -571,12 +578,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?; } "WaitForSingleObject" => { - let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, timeout] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.WaitForSingleObject(handle, timeout, dest)?; } "GetCurrentProcess" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this), @@ -584,7 +592,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "GetCurrentThread" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.write_scalar( Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this), @@ -592,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } "SetThreadDescription" => { - let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "SetThreadDescription")?; let name = this.read_wide_str(this.read_pointer(name)?)?; @@ -607,7 +615,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(0), dest)?; } "GetThreadDescription" => { - let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, name_ptr] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "GetThreadDescription")?; let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name @@ -630,7 +639,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "GetThreadId" => { - let [handle] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let handle = this.read_handle(handle, "GetThreadId")?; let thread = match handle { Handle::Thread(thread) => thread, @@ -641,7 +650,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u32(tid), dest)?; } "GetCurrentThreadId" => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let thread = this.active_thread(); let tid = this.get_tid(thread); this.write_scalar(Scalar::from_u32(tid), dest)?; @@ -649,7 +658,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Miscellaneous "ExitProcess" => { - let [code] = this.check_shim(abi, sys_conv, link_name, args)?; + let [code] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Windows technically uses u32, but we unify everything to a Unix-style i32. let code = this.read_scalar(code)?.to_i32()?; throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false }); @@ -657,7 +666,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "SystemFunction036" => { // used by getrandom 0.1 // This is really 'RtlGenRandom'. - let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_scalar(len)?.to_u32()?; this.gen_random(ptr, len.into())?; @@ -665,7 +674,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "ProcessPrng" => { // used by `std` - let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?; + let [ptr, len] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ptr = this.read_pointer(ptr)?; let len = this.read_target_usize(len)?; this.gen_random(ptr, len)?; @@ -674,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "BCryptGenRandom" => { // used by getrandom 0.2 let [algorithm, ptr, len, flags] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let algorithm = this.read_scalar(algorithm)?; let algorithm = algorithm.to_target_usize(this)?; let ptr = this.read_pointer(ptr)?; @@ -708,7 +717,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetConsoleScreenBufferInfo" => { // `term` needs this, so we fake it. - let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?; + let [console, buffer_info] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(console)?; // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std this.deref_pointer(buffer_info)?; @@ -717,13 +727,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_null(dest)?; } "GetStdHandle" => { - let [which] = this.check_shim(abi, sys_conv, link_name, args)?; + let [which] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.GetStdHandle(which)?; this.write_scalar(res, dest)?; } "DuplicateHandle" => { let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.DuplicateHandle( src_proc, src_handle, @@ -736,14 +746,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(res, dest)?; } "CloseHandle" => { - let [handle] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let ret = this.CloseHandle(handle)?; this.write_scalar(ret, dest)?; } "GetModuleFileNameW" => { - let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?; + let [handle, filename, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.check_no_isolation("`GetModuleFileNameW`")?; let handle = this.read_handle(handle, "GetModuleFileNameW")?; @@ -777,7 +788,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "FormatMessageW" => { let [flags, module, message_id, language_id, buffer, size, arguments] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let flags = this.read_scalar(flags)?.to_u32()?; let _module = this.read_pointer(module)?; // seems to contain a module name @@ -812,26 +823,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. "GetProcessHeap" if this.frame_in_std() => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Just fake a HANDLE // It's fine to not use the Handle type here because its a stub this.write_int(1, dest)?; } "GetModuleHandleA" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_lpModuleName] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // We need to return something non-null here to make `compat_fn!` work. this.write_int(1, dest)?; } "SetConsoleTextAttribute" if this.frame_in_std() => { #[allow(non_snake_case)] let [_hConsoleOutput, _wAttribute] = - this.check_shim(abi, sys_conv, link_name, args)?; + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Pretend these does not exist / nothing happened, by returning zero. this.write_null(dest)?; } "GetConsoleMode" if this.frame_in_std() => { - let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?; + let [console, mode] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.read_target_isize(console)?; this.deref_pointer_as(mode, this.machine.layouts.u32)?; // Indicate an error. @@ -839,25 +852,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } "GetFileType" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_hFile] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Return unknown file type. this.write_null(dest)?; } "AddVectoredExceptionHandler" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_First, _Handler] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } "SetThreadStackGuarantee" if this.frame_in_std() => { #[allow(non_snake_case)] - let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?; + let [_StackSizeInBytes] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; // Any non zero value works for the stdlib. This is just used for stack overflows anyway. this.write_int(1, dest)?; } // this is only callable from std because we know that std ignores the return value "SwitchToThread" if this.frame_in_std() => { - let [] = this.check_shim(abi, sys_conv, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; this.yield_active_thread(); @@ -876,7 +891,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } // This function looks and behaves excatly like miri_start_unwind. - let [payload] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; this.handle_miri_start_unwind(payload)?; return interp_ok(EmulateItemResult::NeedsUnwind); } diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index 058ca24e730f..fdd3e78c6105 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -26,7 +26,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128 "aesdec" | "aesdec.256" | "aesdec.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let key = aes::Block::from(key.to_le_bytes()); let mut state = aes::Block::from(state.to_le_bytes()); @@ -42,7 +43,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128 "aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let mut state = aes::Block::from(state.to_le_bytes()); @@ -66,7 +68,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128 "aesenc" | "aesenc.256" | "aesenc.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let key = aes::Block::from(key.to_le_bytes()); let mut state = aes::Block::from(state.to_le_bytes()); @@ -82,7 +85,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // `state` with the corresponding 128-bit key of `key`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128 "aesenclast" | "aesenclast.256" | "aesenclast.512" => { - let [state, key] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [state, key] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; aes_round(this, state, key, dest, |state, key| { let mut state = aes::Block::from(state.to_le_bytes()); // `aes::hazmat::cipher_round` does the following operations: @@ -102,7 +106,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm_aesimc_si128 function. // Performs the AES InvMixColumns operation on `op` "aesimc" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // Transmute to `u128` let op = op.transmute(this.machine.layouts.u128, this)?; let dest = dest.transmute(this.machine.layouts.u128, this)?; diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 83d23d6ad369..269ce3b51b93 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -33,7 +33,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.ps.256" | "max.ps.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ps.256" => FloatBinOp::Min, @@ -45,7 +46,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement _mm256_min_pd and _mm256_max_pd functions. "min.pd.256" | "max.pd.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.pd.256" => FloatBinOp::Min, @@ -58,21 +60,23 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_round_ps function. // Rounds the elements of `op` according to `rounding`. "round.ps.256" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } // Used to implement the _mm256_round_pd function. // Rounds the elements of `op` according to `rounding`. "round.pd.256" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } // Used to implement _mm256_{rcp,rsqrt}_ps functions. // Performs the operations on all components of `op`. "rcp.ps.256" | "rsqrt.ps.256" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ps.256" => FloatUnaryOp::Rcp, @@ -84,7 +88,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm256_dp_ps function. "dp.ps.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; conditional_dot_product(this, left, right, imm, dest)?; } @@ -92,7 +97,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add/subtract adjacent floating point values // in `left` and `right`. "hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add, @@ -107,7 +113,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and `right`. For each component, returns 0 if false or u32::MAX // if true. "cmp.ps.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -119,7 +126,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and `right`. For each component, returns 0 if false or u64::MAX // if true. "cmp.pd.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -130,7 +138,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and _mm256_cvttpd_epi32 functions. // Converts packed f32/f64 to packed i32. "cvt.ps2dq.256" | "cvtt.ps2dq.256" | "cvt.pd2dq.256" | "cvtt.pd2dq.256" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let rnd = match unprefixed_name { // "current SSE rounding mode", assume nearest @@ -148,7 +156,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // sequence of 4-element arrays, and we shuffle each of these arrays, where // `control` determines which element of the current `data` array is written. "vpermilvar.ps" | "vpermilvar.ps.256" => { - let [data, control] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [data, control] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (data, data_len) = this.project_to_simd(data)?; let (control, control_len) = this.project_to_simd(control)?; @@ -181,7 +190,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // where `right` determines which element of the current `left` array is // written. "vpermilvar.pd" | "vpermilvar.pd.256" => { - let [data, control] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [data, control] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (data, data_len) = this.project_to_simd(data)?; let (control, control_len) = this.project_to_simd(control)?; @@ -213,7 +223,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For each 128-bit element of `dest`, copies one from `left`, `right` or // zero, according to `imm`. "vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(dest.layout, left.layout); assert_eq!(dest.layout, right.layout); @@ -256,7 +267,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is // loaded. "maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => { - let [ptr, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_load(this, ptr, mask, dest)?; } @@ -266,7 +277,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is stored into `ptr.wapping_add(i)`. // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores. "maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => { - let [ptr, mask, value] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask, value] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_store(this, ptr, mask, value)?; } @@ -276,7 +288,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the data crosses a cache line, but for Miri this is just a regular // unaligned read. "ldu.dq.256" => { - let [src_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let src_ptr = this.read_pointer(src_ptr)?; let dest = dest.force_mplace(this)?; @@ -288,7 +300,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Tests `op & mask == 0`, `op & mask == mask` or // `op & mask != 0 && op & mask != mask` "ptestz.256" | "ptestc.256" | "ptestnzc.256" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (all_zero, masked_set) = test_bits_masked(this, op, mask)?; let res = match unprefixed_name { @@ -311,7 +323,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd" | "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256" | "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (direct, negated) = test_high_bits_masked(this, op, mask)?; let res = match unprefixed_name { @@ -333,7 +345,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // compiler, making these functions no-ops. // The only thing that needs to be ensured is the correct calling convention. - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; } _ => return interp_ok(EmulateItemResult::NotSupported), } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 49d5977078b0..ca80c0eba1e5 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -28,7 +28,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_abs_epi{8,16,32} functions. // Calculates the absolute value of packed 8/16/32-bit integers. "pabs.b" | "pabs.w" | "pabs.d" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; int_abs(this, op, dest)?; } @@ -36,7 +36,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add / add with saturation / subtract adjacent 16/32-bit // integer values in `left` and `right`. "phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (which, saturating) = match unprefixed_name { "phadd.w" | "phadd.d" => (mir::BinOp::Add, false), @@ -57,7 +58,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { | "gather.d.pd.256" | "gather.q.pd" | "gather.q.pd.256" | "gather.d.ps" | "gather.d.ps.256" | "gather.q.ps" | "gather.q.ps.256" => { let [src, slice, offsets, mask, scale] = - this.check_shim(abi, CanonAbi::C, link_name, args)?; + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(dest.layout, src.layout); @@ -114,7 +115,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // intermediate signed 32-bit integers. Horizontally add adjacent pairs of // intermediate 32-bit integers, and pack the results in `dest`. "pmadd.wd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -150,7 +152,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the saturating sum of the products with indices `2*i` and `2*i+1` // produces the output at index `i`. "pmadd.ub.sw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -184,7 +187,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is loaded from `ptr.wrapping_add(i)`, otherwise zero is // loaded. "maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => { - let [ptr, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_load(this, ptr, mask, dest)?; } @@ -194,7 +197,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is one, it is stored into `ptr.wapping_add(i)`. // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores. "maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => { - let [ptr, mask, value] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [ptr, mask, value] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mask_store(this, ptr, mask, value)?; } @@ -205,7 +209,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // offsets specified in `imm`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8 "mpsadbw" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mpsadbw(this, left, right, imm, dest)?; } @@ -216,7 +221,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // 1 and then taking the bits `1..=16`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16 "pmul.hr.sw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pmulhrsw(this, left, right, dest)?; } @@ -224,7 +230,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit integer vectors to a single 8-bit integer // vector with signed saturation. "packsswb" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packsswb(this, left, right, dest)?; } @@ -232,7 +239,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 32-bit integer vectors to a single 16-bit integer // vector with signed saturation. "packssdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packssdw(this, left, right, dest)?; } @@ -240,7 +248,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit signed integer vectors to a single 8-bit // unsigned integer vector with saturation. "packuswb" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packuswb(this, left, right, dest)?; } @@ -248,7 +257,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Concatenates two 32-bit signed integer vectors and converts // the result to a 16-bit unsigned integer vector with saturation. "packusdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packusdw(this, left, right, dest)?; } @@ -257,7 +267,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles `left` using the three low bits of each element of `right` // as indices. "permd" | "permps" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -277,7 +288,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm256_permute2x128_si256 function. // Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern. "vperm2i128" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; assert_eq!(left.layout.size.bits(), 256); assert_eq!(right.layout.size.bits(), 256); @@ -314,7 +326,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // in `dest`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8 "psad.bw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -346,7 +359,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles bytes from `left` using `right` as pattern. // Each 128-bit block is shuffled independently. "pshuf.b" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -377,7 +391,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is writen to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b" | "psign.w" | "psign.d" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; psign(this, left, right, dest)?; } @@ -391,7 +406,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is copied to remaining bits. "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" | "psrl.q" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, @@ -406,7 +422,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // (except _mm{,256}_srav_epi64, which are not available in AVX2). "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256" | "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left, diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index 80b1b2e16e60..140e31cc5133 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -35,7 +35,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_scalar(left)?; let right = this.read_scalar(right)?; diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs index f83ce560c84e..9a98a80d6dcc 100644 --- a/src/tools/miri/src/shims/x86/gfni.rs +++ b/src/tools/miri/src/shims/x86/gfni.rs @@ -31,14 +31,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // See `affine_transform` for details. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_ "vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => { - let [left, right, imm8] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm8] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; affine_transform(this, left, right, imm8, dest, /* inverse */ false)?; } // Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions. // See `affine_transform` for details. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv "vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => { - let [left, right, imm8] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm8] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; affine_transform(this, left, right, imm8, dest, /* inverse */ true)?; } // Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions. @@ -47,7 +49,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul "vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; let (dest, dest_len) = this.project_to_simd(dest)?; diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index fbfe459711e0..3324b7b024ac 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -45,7 +45,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [cb_in, a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [cb_in, a, b] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let op = if unprefixed_name.starts_with("add") { mir::BinOp::AddWithOverflow } else { @@ -67,7 +68,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { if is_u64 && this.tcx.sess.target.arch != "x86_64" { return interp_ok(EmulateItemResult::NotSupported); } - let [c_in, a, b, out] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [c_in, a, b, out] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let out = this.deref_pointer_as( out, if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 }, @@ -84,7 +86,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the instruction behaves like a no-op, so it is always safe to call the // intrinsic. "sse2.pause" => { - let [] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; // Only exhibit the spin-loop hint behavior when SSE2 is enabled. if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { this.yield_active_thread(); @@ -103,7 +105,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { len = 8; } - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pclmulqdq(this, left, right, imm, dest, len)?; } diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index d37fad3e6c75..00fe58119e47 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -53,7 +53,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { match unprefixed_name { // Used to implement the _mm_sha256rnds2_epu32 function. "256rnds2" => { - let [a, b, k] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b, k] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; @@ -74,7 +74,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm_sha256msg1_epu32 function. "256msg1" => { - let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; @@ -92,7 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // Used to implement the _mm_sha256msg2_epu32 function. "256msg2" => { - let [a, b] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [a, b] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (a_reg, a_len) = this.project_to_simd(a)?; let (b_reg, b_len) = this.project_to_simd(b)?; diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 1ec15d609c68..6d8def5b53fc 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -34,7 +34,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Performs the operations on the first component of `left` and // `right` and copies the remaining components from `left`. "min.ss" | "max.ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ss" => FloatBinOp::Min, @@ -50,7 +51,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.ps" | "max.ps" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.ps" => FloatBinOp::Min, @@ -64,7 +66,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Performs the operations on the first component of `op` and // copies the remaining components from `op`. "rcp.ss" | "rsqrt.ss" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ss" => FloatUnaryOp::Rcp, @@ -77,7 +79,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement _mm_{sqrt,rcp,rsqrt}_ps functions. // Performs the operations on all components of `op`. "rcp.ps" | "rsqrt.ps" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "rcp.ps" => FloatUnaryOp::Rcp, @@ -96,7 +98,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ss are SSE functions // with hard-coded operations. "cmp.ss" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -112,7 +115,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_ps are SSE functions // with hard-coded operations. "cmp.ps" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -125,7 +129,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "comieq.ss" | "comilt.ss" | "comile.ss" | "comigt.ss" | "comige.ss" | "comineq.ss" | "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss" | "ucomineq.ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -153,7 +158,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cvtss_si64 and _mm_cvttss_si64 functions. // Converts the first component of `op` from f32 to i32/i64. "cvtss2si" | "cvttss2si" | "cvtss2si64" | "cvttss2si64" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, _) = this.project_to_simd(op)?; let op = this.read_immediate(&this.project_index(&op, 0)?)?; @@ -181,7 +186,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // are copied from `left`. // https://www.felixcloutier.com/x86/cvtsi2ss "cvtsi2ss" | "cvtsi642ss" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (dest, dest_len) = this.project_to_simd(dest)?; diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index d6052f830771..8f53adfb5ecf 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -41,7 +41,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // intermediate signed 32-bit integers. Horizontally add adjacent pairs of // intermediate 32-bit integers, and pack the results in `dest`. "pmadd.wd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -79,7 +80,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8 "psad.bw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -117,7 +119,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is copied to remaining bits. "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q" | "psrl.q" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left, @@ -132,7 +135,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and _mm_cvttpd_epi32 functions. // Converts packed f32/f64 to packed i32. "cvtps2dq" | "cvttps2dq" | "cvtpd2dq" | "cvttpd2dq" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op_len, _) = op.layout.ty.simd_size_and_type(*this.tcx); let (dest_len, _) = dest.layout.ty.simd_size_and_type(*this.tcx); @@ -169,7 +172,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit integer vectors to a single 8-bit integer // vector with signed saturation. "packsswb.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packsswb(this, left, right, dest)?; } @@ -177,7 +181,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 16-bit signed integer vectors to a single 8-bit // unsigned integer vector with saturation. "packuswb.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packuswb(this, left, right, dest)?; } @@ -185,7 +190,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts two 32-bit integer vectors to a single 16-bit integer // vector with signed saturation. "packssdw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packssdw(this, left, right, dest)?; } @@ -195,7 +201,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.sd" | "max.sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.sd" => FloatBinOp::Min, @@ -211,7 +218,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // matches the IEEE min/max operations, while x86 has different // semantics. "min.pd" | "max.pd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "min.pd" => FloatBinOp::Min, @@ -230,7 +238,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_sd are SSE2 functions // with hard-coded operations. "cmp.sd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -246,7 +255,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cmp{eq,lt,le,gt,ge,neq,nlt,nle,ngt,nge,ord,unord}_pd are SSE2 functions // with hard-coded operations. "cmp.pd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?; @@ -259,7 +269,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "comieq.sd" | "comilt.sd" | "comile.sd" | "comigt.sd" | "comige.sd" | "comineq.sd" | "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd" | "ucomineq.sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -287,7 +298,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // _mm_cvtsd_si64 and _mm_cvttsd_si64 functions. // Converts the first component of `op` from f64 to i32/i64. "cvtsd2si" | "cvttsd2si" | "cvtsd2si64" | "cvttsd2si64" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, _) = this.project_to_simd(op)?; let op = this.read_immediate(&this.project_index(&op, 0)?)?; @@ -313,7 +324,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Converts the first f64/f32 from `right` to f32/f64 and copies // the remaining elements from `left` "cvtsd2ss" | "cvtss2sd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, _) = this.project_to_simd(right)?; diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index ebf3cb5c3ee0..0fd8c3bc389b 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -26,7 +26,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Horizontally add/subtract adjacent floating point values // in `left` and `right`. "hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let which = match unprefixed_name { "hadd.ps" | "hadd.pd" => mir::BinOp::Add, @@ -42,7 +43,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // the data crosses a cache line, but for Miri this is just a regular // unaligned read. "ldu.dq" => { - let [src_ptr] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [src_ptr] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let src_ptr = this.read_pointer(src_ptr)?; let dest = dest.force_mplace(this)?; diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 6797039cf56f..7736b5e443d0 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -28,7 +28,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // bits `4..=5` if `imm`, and `i`th bit specifies whether element // `i` is zeroed. "insertps" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -63,7 +64,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Concatenates two 32-bit signed integer vectors and converts // the result to a 16-bit unsigned integer vector with saturation. "packusdw" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; packusdw(this, left, right, dest)?; } @@ -73,7 +75,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // products, and conditionally stores the sum in `dest` using the low // 4 bits of `imm`. "dpps" | "dppd" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; conditional_dot_product(this, left, right, imm, dest)?; } @@ -81,14 +84,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // functions. Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. "round.ss" => { - let [left, right, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_first::(this, left, right, rounding, dest)?; } // Used to implement the _mm_floor_ps, _mm_ceil_ps and _mm_round_ps // functions. Rounds the elements of `op` according to `rounding`. "round.ps" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } @@ -96,14 +101,16 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // functions. Rounds the first element of `right` according to `rounding` // and copies the remaining elements from `left`. "round.sd" => { - let [left, right, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_first::(this, left, right, rounding, dest)?; } // Used to implement the _mm_floor_pd, _mm_ceil_pd and _mm_round_pd // functions. Rounds the elements of `op` according to `rounding`. "round.pd" => { - let [op, rounding] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, rounding] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; round_all::(this, op, rounding, dest)?; } @@ -111,7 +118,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Find the minimum unsinged 16-bit integer in `op` and // returns its value and position. "phminposuw" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (op, op_len) = this.project_to_simd(op)?; let (dest, dest_len) = this.project_to_simd(dest)?; @@ -145,7 +152,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // offsets specified in `imm`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8 "mpsadbw" => { - let [left, right, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; mpsadbw(this, left, right, imm, dest)?; } @@ -154,7 +162,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Tests `(op & mask) == 0`, `(op & mask) == mask` or // `(op & mask) != 0 && (op & mask) != mask` "ptestz" | "ptestc" | "ptestnzc" => { - let [op, mask] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op, mask] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (all_zero, masked_set) = test_bits_masked(this, op, mask)?; let res = match unprefixed_name { diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index 7e1e1482ef47..72c5039a12d3 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -222,7 +222,8 @@ fn deconstruct_args<'tcx>( }; if is_explicit { - let [str1, len1, str2, len2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, len1, str2, len2, imm] = + ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = ecx.read_scalar(imm)?.to_u8()?; let default_len = default_len::(imm); @@ -235,7 +236,7 @@ fn deconstruct_args<'tcx>( interp_ok((str1, str2, Some((len1, len2)), imm)) } else { - let [str1, str2, imm] = ecx.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, str2, imm] = ecx.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = ecx.read_scalar(imm)?.to_u8()?; let array_layout = array_layout_fn(ecx, imm)?; @@ -385,7 +386,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // search for a null terminator (see `deconstruct_args` for more details). // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925 "pcmpistriz128" | "pcmpistris128" => { - let [str1, str2, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [str1, str2, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let imm = this.read_scalar(imm)?.to_u8()?; let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 }; @@ -405,7 +407,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // than 16 for byte-sized operands or 8 for word-sized operands. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047 "pcmpestriz128" | "pcmpestris128" => { - let [_, len1, _, len2, imm] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [_, len1, _, len2, imm] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 }; let len = this.read_scalar(len)?.to_i32()?; let imm = this.read_scalar(imm)?.to_u8()?; @@ -432,7 +435,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(EmulateItemResult::NotSupported); } - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let left = this.read_scalar(left)?; let right = this.read_scalar(right)?; diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 310d6b8f765a..52ad6bd44199 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -25,7 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Used to implement the _mm_abs_epi{8,16,32} functions. // Calculates the absolute value of packed 8/16/32-bit integers. "pabs.b.128" | "pabs.w.128" | "pabs.d.128" => { - let [op] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [op] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; int_abs(this, op, dest)?; } @@ -33,7 +33,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Shuffles bytes from `left` using `right` as pattern. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8 "pshuf.b.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -62,7 +63,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // integer values in `left` and `right`. "phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128" | "phsub.d.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (which, saturating) = match unprefixed_name { "phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false), @@ -81,7 +83,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // produces the output at index `i`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16 "pmadd.ub.sw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; let (left, left_len) = this.project_to_simd(left)?; let (right, right_len) = this.project_to_simd(right)?; @@ -116,7 +119,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // 1 and then taking the bits `1..=16`. // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16 "pmul.hr.sw.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; pmulhrsw(this, left, right, dest)?; } @@ -126,7 +130,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // is writen to the corresponding output element. // Basically, we multiply `left` with `right.signum()`. "psign.b.128" | "psign.w.128" | "psign.d.128" => { - let [left, right] = this.check_shim(abi, CanonAbi::C, link_name, args)?; + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; psign(this, left, right, dest)?; } From 71ea00f7a3911990e9e5328446312bc0d792cf5f Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 14:07:10 +0200 Subject: [PATCH 092/201] fix: Fix runnables extra env not substituting env vars --- .../rust-analyzer/editors/code/src/config.ts | 45 +++++++++---------- .../rust-analyzer/editors/code/src/debug.ts | 24 +++++----- .../rust-analyzer/editors/code/src/run.ts | 14 +++--- .../rust-analyzer/editors/code/src/tasks.ts | 12 ++++- .../editors/code/src/toolchain.ts | 8 ++-- 5 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index d2dc740c09b5..cadab37f5174 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -8,10 +8,9 @@ import type { Disposable } from "vscode"; export type RunnableEnvCfgItem = { mask?: string; - env: Record; + env: { [key: string]: { toString(): string } | null }; platform?: string | string[]; }; -export type RunnableEnvCfg = Record | RunnableEnvCfgItem[]; type ShowStatusBar = "always" | "never" | { documentSelector: vscode.DocumentSelector }; @@ -261,18 +260,9 @@ export class Config { return this.get("testExplorer"); } - runnablesExtraEnv(label: string): Record | undefined { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const item = this.get("runnables.extraEnv") ?? this.get("runnableEnv"); - if (!item) return undefined; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const fixRecord = (r: Record) => { - for (const key in r) { - if (typeof r[key] !== "string") { - r[key] = String(r[key]); - } - } - }; + runnablesExtraEnv(label: string): Env { + let extraEnv = this.get("runnables.extraEnv") ?? {}; + if (!extraEnv) return {}; const platform = process.platform; const checkPlatform = (it: RunnableEnvCfgItem) => { @@ -283,19 +273,24 @@ export class Config { return true; }; - if (item instanceof Array) { + if (extraEnv instanceof Array) { const env = {}; - for (const it of item) { + for (const it of extraEnv) { const masked = !it.mask || new RegExp(it.mask).test(label); if (masked && checkPlatform(it)) { Object.assign(env, it.env); } } - fixRecord(env); - return env; + extraEnv = env; } - fixRecord(item); - return item; + return substituteVariablesInEnv( + Object.fromEntries( + Object.entries(extraEnv).map(([k, v]) => [ + k, + typeof v === "string" ? v : v?.toString(), + ]), + ), + ); } get restartServerOnConfigChange() { @@ -490,11 +485,11 @@ function computeVscodeVar(varName: string): string | null { folder === undefined ? "" // no workspace opened : // could use currently opened document to detect the correct - // workspace. However, that would be determined by the document - // user has opened on Editor startup. Could lead to - // unpredictable workspace selection in practice. - // It's better to pick the first one - normalizeDriveLetter(folder.uri.fsPath); + // workspace. However, that would be determined by the document + // user has opened on Editor startup. Could lead to + // unpredictable workspace selection in practice. + // It's better to pick the first one + normalizeDriveLetter(folder.uri.fsPath); return fsPath; }; // https://code.visualstudio.com/docs/editor/variables-reference diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index adb75c23c70c..9559fe16f517 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -6,7 +6,7 @@ import type * as ra from "./lsp_ext"; import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; -import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter } from "./util"; +import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter, Env } from "./util"; import type { Config } from "./config"; // Here we want to keep track on everything that's currently running @@ -108,9 +108,9 @@ async function getDebugConfiguration( await vscode.window.showErrorMessage( `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` + - `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` + - `, [C/C++](command:${commandCCpp} "Open C/C++") ` + - `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, + `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` + + `, [C/C++](command:${commandCCpp} "Open C/C++") ` + + `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, ); return; } @@ -124,7 +124,7 @@ async function getDebugConfiguration( !isMultiFolderWorkspace || !runnableArgs.workspaceRoot ? firstWorkspace : workspaceFolders.find((w) => runnableArgs.workspaceRoot?.includes(w.uri.fsPath)) || - firstWorkspace; + firstWorkspace; const workspace = unwrapUndefinable(maybeWorkspace); const wsFolder = normalizeDriveLetter(path.normalize(workspace.uri.fsPath)); @@ -207,7 +207,7 @@ type SourceFileMap = { }; async function discoverSourceFileMap( - env: Record, + env: Env, cwd: string, ): Promise { const sysroot = env["RUSTC_TOOLCHAIN"]; @@ -232,7 +232,7 @@ type PropertyFetcher = ( type DebugConfigProvider> = { executableProperty: keyof DebugConfig; - environmentProperty: PropertyFetcher, keyof DebugConfig>; + environmentProperty: PropertyFetcher; runnableArgsProperty: PropertyFetcher; sourceFileMapProperty?: keyof DebugConfig; type: Type; @@ -276,7 +276,7 @@ const knownEngines: { "environment", Object.entries(env).map((entry) => ({ name: entry[0], - value: entry[1], + value: entry[1] ?? "", })), ], runnableArgsProperty: (runnableArgs: ra.CargoRunnableArgs) => [ @@ -306,7 +306,7 @@ const knownEngines: { async function getDebugExecutable( runnableArgs: ra.CargoRunnableArgs, - env: Record, + env: Env, ): Promise { const cargo = new Cargo(runnableArgs.workspaceRoot || ".", env); const executable = await cargo.executableFromArgs(runnableArgs); @@ -328,7 +328,7 @@ function getDebugConfig( runnable: ra.Runnable, runnableArgs: ra.CargoRunnableArgs, executable: string, - env: Record, + env: Env, sourceFileMap?: Record, ): vscode.DebugConfiguration { const { @@ -380,14 +380,14 @@ type CodeLldbDebugConfig = { args: string[]; sourceMap: Record | undefined; sourceLanguages: ["rust"]; - env: Record; + env: Env; } & BaseDebugConfig<"lldb">; type NativeDebugConfig = { target: string; // See https://github.com/WebFreak001/code-debug/issues/359 arguments: string; - env: Record; + env: Env; valuesFormatting: "prettyPrinters"; } & BaseDebugConfig<"gdb">; diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 95166c427b2b..bed874705d8a 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -7,7 +7,7 @@ import type { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; import type { Config } from "./config"; import type { LanguageClient } from "vscode-languageclient/node"; -import { log, unwrapUndefinable, type RustEditor } from "./util"; +import { Env, log, unwrapUndefinable, type RustEditor } from "./util"; const quickPickButtons = [ { iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configuration." }, @@ -124,9 +124,9 @@ export class RunnableQuickPick implements vscode.QuickPickItem { export function prepareBaseEnv( inheritEnv: boolean, - base?: Record, -): Record { - const env: Record = { RUST_BACKTRACE: "short" }; + base?: Env, +): Env { + const env: Env = { RUST_BACKTRACE: "short" }; if (inheritEnv) { Object.assign(env, process.env); } @@ -138,9 +138,9 @@ export function prepareBaseEnv( export function prepareEnv( inheritEnv: boolean, - runnableEnv?: Record, - runnableEnvCfg?: Record, -): Record { + runnableEnv?: Env, + runnableEnvCfg?: Env, +): Env { const env = prepareBaseEnv(inheritEnv, runnableEnv); if (runnableEnvCfg) { diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index 730ec6d1e90c..417c28f0ee8f 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import type { Config } from "./config"; import * as toolchain from "./toolchain"; +import { Env } from "./util"; // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and // our configuration should be compatible with it so use the same key. @@ -117,8 +118,8 @@ export async function buildRustTask( export async function targetToExecution( definition: TaskDefinition, options?: { - env?: { [key: string]: string }; cwd?: string; + env?: Env; }, cargo?: string, ): Promise { @@ -131,7 +132,14 @@ export async function targetToExecution( command = definition.command; args = definition.args || []; } - return new vscode.ProcessExecution(command, args, options); + return new vscode.ProcessExecution(command, args, { + cwd: options?.cwd, + env: + Object.fromEntries( + Object.entries(options?.env ?? {}).map(([key, value]) => [key, value ?? ""]) + ) + , + }); } export function activateTaskProvider(config: Config): vscode.Disposable { diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index a859ce6ff007..f64dec2ccfb7 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -3,7 +3,7 @@ import * as os from "os"; import * as path from "path"; import * as readline from "readline"; import * as vscode from "vscode"; -import { log, memoizeAsync, unwrapUndefinable } from "./util"; +import { Env, log, memoizeAsync, unwrapUndefinable } from "./util"; import type { CargoRunnableArgs } from "./lsp_ext"; interface CompilationArtifact { @@ -37,8 +37,8 @@ interface CompilerMessage { export class Cargo { constructor( readonly rootFolder: string, - readonly env: Record, - ) {} + readonly env: Env, + ) { } // Made public for testing purposes static artifactSpec(cargoArgs: string[], executableArgs?: string[]): ArtifactSpec { @@ -156,7 +156,7 @@ export class Cargo { /** Mirrors `toolchain::cargo()` implementation */ // FIXME: The server should provide this -export function cargoPath(env?: Record): Promise { +export function cargoPath(env?: Env): Promise { if (env?.["RUSTC_TOOLCHAIN"]) { return Promise.resolve("cargo"); } From 7a767d01cf7ed75c6924a96cc90d9191b447e76b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 14:09:21 +0200 Subject: [PATCH 093/201] Unherit server extra env for runnables extra env --- .../rust-analyzer/editors/code/src/config.ts | 21 +++++++++------ .../rust-analyzer/editors/code/src/debug.ts | 27 ++++++++++--------- .../rust-analyzer/editors/code/src/run.ts | 11 ++------ .../rust-analyzer/editors/code/src/tasks.ts | 8 +++--- .../editors/code/src/toolchain.ts | 2 +- 5 files changed, 33 insertions(+), 36 deletions(-) diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts index cadab37f5174..3b1b0768d3cf 100644 --- a/src/tools/rust-analyzer/editors/code/src/config.ts +++ b/src/tools/rust-analyzer/editors/code/src/config.ts @@ -261,8 +261,12 @@ export class Config { } runnablesExtraEnv(label: string): Env { - let extraEnv = this.get("runnables.extraEnv") ?? {}; - if (!extraEnv) return {}; + const serverEnv = this.serverExtraEnv; + let extraEnv = + this.get< + RunnableEnvCfgItem[] | { [key: string]: { toString(): string } | null } | null + >("runnables.extraEnv") ?? {}; + if (!extraEnv) return serverEnv; const platform = process.platform; const checkPlatform = (it: RunnableEnvCfgItem) => { @@ -283,7 +287,7 @@ export class Config { } extraEnv = env; } - return substituteVariablesInEnv( + const runnableExtraEnv = substituteVariablesInEnv( Object.fromEntries( Object.entries(extraEnv).map(([k, v]) => [ k, @@ -291,6 +295,7 @@ export class Config { ]), ), ); + return { ...runnableExtraEnv, ...serverEnv }; } get restartServerOnConfigChange() { @@ -485,11 +490,11 @@ function computeVscodeVar(varName: string): string | null { folder === undefined ? "" // no workspace opened : // could use currently opened document to detect the correct - // workspace. However, that would be determined by the document - // user has opened on Editor startup. Could lead to - // unpredictable workspace selection in practice. - // It's better to pick the first one - normalizeDriveLetter(folder.uri.fsPath); + // workspace. However, that would be determined by the document + // user has opened on Editor startup. Could lead to + // unpredictable workspace selection in practice. + // It's better to pick the first one + normalizeDriveLetter(folder.uri.fsPath); return fsPath; }; // https://code.visualstudio.com/docs/editor/variables-reference diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts index 9559fe16f517..24f8d9087300 100644 --- a/src/tools/rust-analyzer/editors/code/src/debug.ts +++ b/src/tools/rust-analyzer/editors/code/src/debug.ts @@ -6,7 +6,14 @@ import type * as ra from "./lsp_ext"; import { Cargo } from "./toolchain"; import type { Ctx } from "./ctx"; import { createTaskFromRunnable, prepareEnv } from "./run"; -import { execute, isCargoRunnableArgs, unwrapUndefinable, log, normalizeDriveLetter, Env } from "./util"; +import { + execute, + isCargoRunnableArgs, + unwrapUndefinable, + log, + normalizeDriveLetter, + Env, +} from "./util"; import type { Config } from "./config"; // Here we want to keep track on everything that's currently running @@ -108,9 +115,9 @@ async function getDebugConfiguration( await vscode.window.showErrorMessage( `Install [CodeLLDB](command:${commandCodeLLDB} "Open CodeLLDB")` + - `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` + - `, [C/C++](command:${commandCCpp} "Open C/C++") ` + - `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, + `, [lldb-dap](command:${commandLLDBDap} "Open lldb-dap")` + + `, [C/C++](command:${commandCCpp} "Open C/C++") ` + + `or [Native Debug](command:${commandNativeDebug} "Open Native Debug") for debugging.`, ); return; } @@ -124,7 +131,7 @@ async function getDebugConfiguration( !isMultiFolderWorkspace || !runnableArgs.workspaceRoot ? firstWorkspace : workspaceFolders.find((w) => runnableArgs.workspaceRoot?.includes(w.uri.fsPath)) || - firstWorkspace; + firstWorkspace; const workspace = unwrapUndefinable(maybeWorkspace); const wsFolder = normalizeDriveLetter(path.normalize(workspace.uri.fsPath)); @@ -206,10 +213,7 @@ type SourceFileMap = { destination: string; }; -async function discoverSourceFileMap( - env: Env, - cwd: string, -): Promise { +async function discoverSourceFileMap(env: Env, cwd: string): Promise { const sysroot = env["RUSTC_TOOLCHAIN"]; if (sysroot) { // let's try to use the default toolchain @@ -304,10 +308,7 @@ const knownEngines: { }, }; -async function getDebugExecutable( - runnableArgs: ra.CargoRunnableArgs, - env: Env, -): Promise { +async function getDebugExecutable(runnableArgs: ra.CargoRunnableArgs, env: Env): Promise { const cargo = new Cargo(runnableArgs.workspaceRoot || ".", env); const executable = await cargo.executableFromArgs(runnableArgs); diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index bed874705d8a..87c1d529f7ee 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -122,10 +122,7 @@ export class RunnableQuickPick implements vscode.QuickPickItem { } } -export function prepareBaseEnv( - inheritEnv: boolean, - base?: Env, -): Env { +export function prepareBaseEnv(inheritEnv: boolean, base?: Env): Env { const env: Env = { RUST_BACKTRACE: "short" }; if (inheritEnv) { Object.assign(env, process.env); @@ -136,11 +133,7 @@ export function prepareBaseEnv( return env; } -export function prepareEnv( - inheritEnv: boolean, - runnableEnv?: Env, - runnableEnvCfg?: Env, -): Env { +export function prepareEnv(inheritEnv: boolean, runnableEnv?: Env, runnableEnvCfg?: Env): Env { const env = prepareBaseEnv(inheritEnv, runnableEnv); if (runnableEnvCfg) { diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index 417c28f0ee8f..eb0748a704bf 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -134,11 +134,9 @@ export async function targetToExecution( } return new vscode.ProcessExecution(command, args, { cwd: options?.cwd, - env: - Object.fromEntries( - Object.entries(options?.env ?? {}).map(([key, value]) => [key, value ?? ""]) - ) - , + env: Object.fromEntries( + Object.entries(options?.env ?? {}).map(([key, value]) => [key, value ?? ""]), + ), }); } diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts index f64dec2ccfb7..06f75a8f8d65 100644 --- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts +++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts @@ -38,7 +38,7 @@ export class Cargo { constructor( readonly rootFolder: string, readonly env: Env, - ) { } + ) {} // Made public for testing purposes static artifactSpec(cargoArgs: string[], executableArgs?: string[]): ArtifactSpec { From da1991d56513106298bbb5147a94a3782de14b85 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 15:41:42 +0200 Subject: [PATCH 094/201] check_static_item: explain should_check_for_sync choices --- compiler/rustc_hir_analysis/src/check/check.rs | 4 +++- compiler/rustc_hir_analysis/src/collect/type_of.rs | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index acc831d30ca0..6e63ce310243 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -768,7 +768,9 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), check_static_inhabited(tcx, def_id); check_static_linkage(tcx, def_id); let ty = tcx.type_of(def_id).instantiate_identity(); - res = res.and(wfcheck::check_static_item(tcx, def_id, ty, true)); + res = res.and(wfcheck::check_static_item( + tcx, def_id, ty, /* should_check_for_sync */ true, + )); } DefKind::Const => res = res.and(wfcheck::check_const_item(tcx, def_id)), _ => unreachable!(), diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index 68a91212e504..22fb02714dd7 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -221,7 +221,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let ty = icx.lower_ty(ty); // MIR relies on references to statics being scalars. // Verify that here to avoid ill-formed MIR. - match check_static_item(tcx, def_id, ty, false) { + // We skip the `Sync` check to avoid cycles for type-alias-impl-trait, + // relying on the fact that non-Sync statics don't ICE the rest of the compiler. + match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) { Ok(()) => ty, Err(guar) => Ty::new_error(tcx, guar), } @@ -286,7 +288,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_ let ty = icx.lower_ty(ty); // MIR relies on references to statics being scalars. // Verify that here to avoid ill-formed MIR. - match check_static_item(tcx, def_id, ty, false) { + // We skip the `Sync` check to avoid cycles for type-alias-impl-trait, + // relying on the fact that non-Sync statics don't ICE the rest of the compiler. + match check_static_item(tcx, def_id, ty, /* should_check_for_sync */ false) { Ok(()) => ty, Err(guar) => Ty::new_error(tcx, guar), } From 41259050afa0a2e66d739594a27b638266706074 Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Fri, 14 Mar 2025 09:58:46 +0100 Subject: [PATCH 095/201] Add support for building and linking against genmc --- src/tools/miri/.gitignore | 1 - src/tools/miri/Cargo.lock | 545 ++++++++++++++++++ src/tools/miri/Cargo.toml | 6 +- src/tools/miri/doc/genmc.md | 78 +++ src/tools/miri/etc/rust_analyzer_helix.toml | 1 + src/tools/miri/etc/rust_analyzer_vscode.json | 1 + src/tools/miri/genmc-sys/.gitignore | 1 + src/tools/miri/genmc-sys/Cargo.toml | 17 + src/tools/miri/genmc-sys/build.rs | 268 +++++++++ src/tools/miri/genmc-sys/src/lib.rs | 30 + .../miri/genmc-sys/src_cpp/MiriInterface.cpp | 50 ++ .../miri/genmc-sys/src_cpp/MiriInterface.hpp | 44 ++ src/tools/miri/miri-script/src/commands.rs | 4 +- src/tools/miri/src/bin/miri.rs | 36 +- .../miri/src/concurrency/genmc/config.rs | 32 +- src/tools/miri/src/concurrency/genmc/dummy.rs | 20 +- src/tools/miri/src/concurrency/genmc/mod.rs | 17 +- src/tools/miri/src/concurrency/mod.rs | 12 +- src/tools/miri/src/concurrency/thread.rs | 2 - src/tools/miri/src/eval.rs | 6 +- src/tools/miri/src/machine.rs | 5 +- .../miri/tests/genmc/pass/test_cxx_build.rs | 8 + .../tests/genmc/pass/test_cxx_build.stderr | 6 + src/tools/miri/tests/ui.rs | 14 + 24 files changed, 1161 insertions(+), 43 deletions(-) create mode 100644 src/tools/miri/doc/genmc.md create mode 100644 src/tools/miri/genmc-sys/.gitignore create mode 100644 src/tools/miri/genmc-sys/Cargo.toml create mode 100644 src/tools/miri/genmc-sys/build.rs create mode 100644 src/tools/miri/genmc-sys/src/lib.rs create mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp create mode 100644 src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp create mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.rs create mode 100644 src/tools/miri/tests/genmc/pass/test_cxx_build.stderr diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore index ed2d0ba7ba07..4a238dc03134 100644 --- a/src/tools/miri/.gitignore +++ b/src/tools/miri/.gitignore @@ -1,5 +1,4 @@ target -/doc tex/*/out *.dot *.out diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index 0af4181dc154..ece51f2ba74a 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -170,6 +170,8 @@ version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -214,6 +216,52 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.1", +] + [[package]] name = "color-eyre" version = "0.6.5" @@ -313,6 +361,68 @@ dependencies = [ "typenum", ] +[[package]] +name = "cxx" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +dependencies = [ + "cc", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "directories" version = "6.0.0" @@ -334,12 +444,29 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "encode_unicode" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.13" @@ -372,6 +499,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -382,6 +524,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "genmc-sys" +version = "0.1.0" +dependencies = [ + "cc", + "cmake", + "cxx", + "cxx-build", + "git2", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -411,12 +564,150 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "git2" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indenter" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "indicatif" version = "0.17.11" @@ -463,6 +754,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -510,6 +811,19 @@ dependencies = [ "cc", ] +[[package]] +name = "libgit2-sys" +version = "0.18.2+1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.8" @@ -530,12 +844,39 @@ dependencies = [ "libc", ] +[[package]] +name = "libz-sys" +version = "1.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b70e7a7df205e92a1a4cd9aaae7898dac0aa555503cc0a649494d0d60e7651d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + [[package]] name = "linux-raw-sys" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + [[package]] name = "lock_api" version = "0.4.13" @@ -612,6 +953,7 @@ dependencies = [ "chrono-tz", "colored 3.0.0", "directories", + "genmc-sys", "getrandom 0.3.3", "ipc-channel", "libc", @@ -672,6 +1014,24 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -722,6 +1082,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "perf-event-open-sys" version = "3.0.0" @@ -755,12 +1121,27 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "portable-atomic" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -946,6 +1327,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" + [[package]] name = "semver" version = "1.0.26" @@ -1025,6 +1412,18 @@ dependencies = [ "color-eyre", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "syn" version = "2.0.104" @@ -1036,6 +1435,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tempfile" version = "3.20.0" @@ -1049,6 +1459,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1108,6 +1527,16 @@ dependencies = [ "libc", ] +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tracing" version = "0.1.41" @@ -1199,6 +1628,23 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "1.17.0" @@ -1216,6 +1662,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1305,6 +1757,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "windows" version = "0.58.0" @@ -1524,6 +1985,36 @@ dependencies = [ "bitflags", ] +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.26" @@ -1543,3 +2034,57 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index d293af5cea20..91dadf78a2f7 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -48,6 +48,10 @@ nix = { version = "0.30.1", features = ["mman", "ptrace", "signal"], optional = ipc-channel = { version = "0.20.0", optional = true } capstone = { version = "0.13", optional = true } +# FIXME(genmc,macos): Add `target_os = "macos"` once https://github.com/dtolnay/cxx/issues/1535 is fixed. +[target.'cfg(all(target_os = "linux", target_pointer_width = "64", target_endian = "little"))'.dependencies] +genmc-sys = { path = "./genmc-sys/", version = "0.1.0", optional = true } + [dev-dependencies] ui_test = "0.30.2" colored = "3" @@ -66,7 +70,7 @@ harness = false [features] default = ["stack-cache", "native-lib"] -genmc = [] +genmc = ["dep:genmc-sys"] # this enables a GPL dependency! stack-cache = [] stack-cache-consistency-check = ["stack-cache"] tracing = ["serde_json"] diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md new file mode 100644 index 000000000000..4839919557ec --- /dev/null +++ b/src/tools/miri/doc/genmc.md @@ -0,0 +1,78 @@ +# **(WIP)** Documentation for Miri-GenMC +[GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. + +**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** + + + +## Usage + +**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", which is NOT compatible with Miri's "MIT OR Apache" license.** + +**IMPORTANT: There should be no distribution of Miri-GenMC until all licensing questions are clarified (the `genmc` feature of Miri is OFF-BY-DEFAULT and should be OFF for all Miri releases).** + +For testing/developing Miri-GenMC (while keeping in mind the licensing issues): +- clone the Miri repo. +- build Miri-GenMC with `./miri build --features=genmc`. +- OR: install Miri-GenMC in the current system with `./miri install --features=genmc` + +Basic usage: +```shell +MIRIFLAGS="-Zmiri-genmc" cargo miri run +``` + + + + + +## Tips + + + +## Limitations + +Some or all of these limitations might get removed in the future: + +- Borrow tracking is currently incompatible (stacked/tree borrows). +- Only Linux is supported for now. +- No 32-bit platform support. +- No cross-platform interpretation. + + + +## Development + +GenMC is written in C++, which complicates development a bit. +For Rust-C++ interop, Miri uses [CXX.rs](https://cxx.rs/), and all handling of C++ code is contained in the `genmc-sys` crate (located in the Miri repository root directory: `miri/genmc-sys/`). + +Building GenMC requires a compiler with C++23 support. + +Currently, building GenMC also requires linking to LLVM, which needs to be installed manually. + +The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with different maintainers). +Note that this repo is just a mirror repo. + + + + +### Building the GenMC Library +The build script in the `genmc-sys` crate handles locating, downloading, building and linking the GenMC library. + +To determine which GenMC repo path will be used, the following steps are taken: +- If the env var `GENMC_SRC_PATH` is set, it's value is used as a path to a directory with a GenMC repo (e.g., `GENMC_SRC_PATH="path/to/miri/genmc-sys/genmc-sys-local"`). + - Note that this variable must be set wherever Miri is built, e.g., in the terminal, or in the Rust Analyzer settings. +- If the path `genmc-sys/genmc-src/genmc` exists, try to set the GenMC repo there to the commit we need. +- If the downloaded repo doesn't exist or is missing the commit, the build script will fetch the commit over the network. + - Note that the build script will *not* access the network if any of the steps previous steps succeeds. + +Once we get the path to the repo, the compilation proceeds in two steps: +- Compile GenMC into a library (using cmake). +- Compile the cxx.rs bridge to connect the library to the Rust code. +The first step is where all build settings are made, the relevant ones are then stored in a `config.h` file that can be included in the second compilation step. + +#### Code Formatting +Note that all directories with names starting with `genmc-src` are ignored by `./miri fmt` on purpose. +GenMC also contains Rust files, but they should not be formatted with Miri's formatting rules. +For working on Miri-GenMC locally, placing the GenMC repo into such a path (e.g., `miri/genmc-sys/genmc-src-local`) ensures that it is also exempt from formatting. + + diff --git a/src/tools/miri/etc/rust_analyzer_helix.toml b/src/tools/miri/etc/rust_analyzer_helix.toml index 91e4070478c8..c46b246049ff 100644 --- a/src/tools/miri/etc/rust_analyzer_helix.toml +++ b/src/tools/miri/etc/rust_analyzer_helix.toml @@ -5,6 +5,7 @@ source = "discover" linkedProjects = [ "Cargo.toml", "cargo-miri/Cargo.toml", + "genmc-sys/Cargo.toml", "miri-script/Cargo.toml", ] diff --git a/src/tools/miri/etc/rust_analyzer_vscode.json b/src/tools/miri/etc/rust_analyzer_vscode.json index 6917c6a1fd85..8e647f5331f0 100644 --- a/src/tools/miri/etc/rust_analyzer_vscode.json +++ b/src/tools/miri/etc/rust_analyzer_vscode.json @@ -3,6 +3,7 @@ "rust-analyzer.linkedProjects": [ "Cargo.toml", "cargo-miri/Cargo.toml", + "genmc-sys/Cargo.toml", "miri-script/Cargo.toml", ], "rust-analyzer.check.invocationStrategy": "once", diff --git a/src/tools/miri/genmc-sys/.gitignore b/src/tools/miri/genmc-sys/.gitignore new file mode 100644 index 000000000000..276a053cd055 --- /dev/null +++ b/src/tools/miri/genmc-sys/.gitignore @@ -0,0 +1 @@ +genmc-src*/ diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml new file mode 100644 index 000000000000..95aef75afc4f --- /dev/null +++ b/src/tools/miri/genmc-sys/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors = ["Miri Team"] +# The parts in this repo are MIT OR Apache-2.0, but we are linking in +# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later. +license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later" +name = "genmc-sys" +version = "0.1.0" +edition = "2024" + +[dependencies] +cxx = { version = "1.0.160", features = ["c++20"] } + +[build-dependencies] +cc = "1.2.30" +cmake = "0.1.54" +git2 = { version = "0.20.2", default-features = false, features = ["https"] } +cxx-build = { version = "1.0.160", features = ["parallel"] } diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs new file mode 100644 index 000000000000..16f7b69cc211 --- /dev/null +++ b/src/tools/miri/genmc-sys/build.rs @@ -0,0 +1,268 @@ +use std::path::{Path, PathBuf}; +use std::str::FromStr; + +// Build script for running Miri with GenMC. +// Check out doc/genmc.md for more info. + +/// Path where the downloaded GenMC repository will be stored (relative to the `genmc-sys` directory). +/// Note that this directory is *not* cleaned up automatically by `cargo clean`. +const GENMC_DOWNLOAD_PATH: &str = "./genmc-src/genmc/"; + +/// Name of the library of the GenMC model checker. +const GENMC_MODEL_CHECKER: &str = "genmc_lib"; + +/// Path where the `cxx_bridge!` macro is used to define the Rust-C++ interface. +const RUST_CXX_BRIDGE_FILE_PATH: &str = "src/lib.rs"; + +/// The profile with which to build GenMC. +const GENMC_CMAKE_PROFILE: &str = "RelWithDebInfo"; + +mod downloading { + use std::path::PathBuf; + use std::str::FromStr; + + use git2::{Commit, Oid, Remote, Repository, StatusOptions}; + + use super::GENMC_DOWNLOAD_PATH; + + /// The GenMC repository the we get our commit from. + pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git"; + /// The GenMC commit we depend on. It must be available on the specified GenMC repository. + pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab"; + + pub(crate) fn download_genmc() -> PathBuf { + let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH); + let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid."); + + match Repository::open(&genmc_download_path) { + Ok(repo) => { + assert_repo_unmodified(&repo); + let commit = update_local_repo(&repo, commit_oid); + checkout_commit(&repo, &commit); + } + Err(_) => { + let repo = clone_remote_repo(&genmc_download_path); + let Ok(commit) = repo.find_commit(commit_oid) else { + panic!( + "Cloned GenMC repository does not contain required commit '{GENMC_COMMIT}'" + ); + }; + checkout_commit(&repo, &commit); + } + }; + + genmc_download_path + } + + fn get_remote(repo: &Repository) -> Remote<'_> { + let remote = repo.find_remote("origin").unwrap_or_else(|e| { + panic!( + "Could not load commit ({GENMC_COMMIT}) from remote repository '{GENMC_GITHUB_URL}'. Error: {e}" + ); + }); + + // Ensure that the correct remote URL is set. + let remote_url = remote.url(); + if let Some(remote_url) = remote_url + && remote_url == GENMC_GITHUB_URL + { + return remote; + } + + // Update remote URL. + println!( + "cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'" + ); + repo.remote_set_url("origin", GENMC_GITHUB_URL) + .expect("cannot rename url of remote 'origin'"); + + // Reacquire the `Remote`, since `remote_set_url` doesn't update Remote objects already in memory. + repo.find_remote("origin").unwrap() + } + + // Check if the required commit exists already, otherwise try fetching it. + fn update_local_repo(repo: &Repository, commit_oid: Oid) -> Commit<'_> { + repo.find_commit(commit_oid).unwrap_or_else(|_find_error| { + println!("GenMC repository at path '{GENMC_DOWNLOAD_PATH}' does not contain commit '{GENMC_COMMIT}'."); + // The commit is not in the checkout. Try `git fetch` and hope that we find the commit then. + let mut remote = get_remote(repo); + remote.fetch(&[GENMC_COMMIT], None, None).expect("Failed to fetch from remote."); + + repo.find_commit(commit_oid) + .expect("Remote repository should contain expected commit") + }) + } + + fn clone_remote_repo(genmc_download_path: &PathBuf) -> Repository { + Repository::clone(GENMC_GITHUB_URL, &genmc_download_path).unwrap_or_else(|e| { + panic!("Cannot clone GenMC repo from '{GENMC_GITHUB_URL}': {e:?}"); + }) + } + + /// Set the state of the repo to a specific commit + fn checkout_commit(repo: &Repository, commit: &Commit<'_>) { + repo.checkout_tree(commit.as_object(), None).expect("Failed to checkout"); + repo.set_head_detached(commit.id()).expect("Failed to set HEAD"); + println!("Successfully set checked out commit {commit:?}"); + } + + /// Check that the downloaded repository is unmodified. + /// If it is modified, explain that it shouldn't be, and hint at how to do local development with GenMC. + /// We don't overwrite any changes made to the directory, to prevent data loss. + fn assert_repo_unmodified(repo: &Repository) { + let statuses = repo + .statuses(Some( + StatusOptions::new() + .include_untracked(true) + .include_ignored(false) + .include_unmodified(false), + )) + .expect("should be able to get repository status"); + if statuses.is_empty() { + return; + } + + println!( + "HINT: For local development, set the environment variable 'GENMC_SRC_PATH' to the path of a GenMC repository.", + ); + panic!( + "Downloaded GenMC repository at path '{GENMC_DOWNLOAD_PATH}' has been modified. Please undo any changes made, or delete the '{GENMC_DOWNLOAD_PATH}' directory to have it downloaded again." + ); + } +} + +// FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed. +/// The linked LLVM version is in the generated `config.h`` file, which we parse and use to link to LLVM. +/// Returns c++ compiler definitions required for building with/including LLVM, and the include path for LLVM headers. +fn link_to_llvm(config_file: &Path) -> (String, String) { + /// Search a string for a line matching `//@VARIABLE_NAME: VARIABLE CONTENT` + fn extract_value<'a>(input: &'a str, name: &str) -> Option<&'a str> { + input + .lines() + .find_map(|line| line.strip_prefix("//@")?.strip_prefix(name)?.strip_prefix(": ")) + } + + let file_content = std::fs::read_to_string(&config_file).unwrap_or_else(|err| { + panic!("GenMC config file ({}) should exist, but got errror {err:?}", config_file.display()) + }); + + let llvm_definitions = extract_value(&file_content, "LLVM_DEFINITIONS") + .expect("Config file should contain LLVM_DEFINITIONS"); + let llvm_include_dirs = extract_value(&file_content, "LLVM_INCLUDE_DIRS") + .expect("Config file should contain LLVM_INCLUDE_DIRS"); + let llvm_library_dir = extract_value(&file_content, "LLVM_LIBRARY_DIR") + .expect("Config file should contain LLVM_LIBRARY_DIR"); + let llvm_config_path = extract_value(&file_content, "LLVM_CONFIG_PATH") + .expect("Config file should contain LLVM_CONFIG_PATH"); + + // Add linker search path. + let lib_dir = PathBuf::from_str(llvm_library_dir).unwrap(); + println!("cargo::rustc-link-search=native={}", lib_dir.display()); + + // Add libraries to link. + let output = std::process::Command::new(llvm_config_path) + .arg("--libs") // Print the libraries to link to (space-separated list) + .output() + .expect("failed to execute llvm-config"); + let llvm_link_libs = + String::try_from(output.stdout).expect("llvm-config output should be a valid string"); + + for link_lib in llvm_link_libs.trim().split(" ") { + let link_lib = + link_lib.strip_prefix("-l").expect("Linker parameter should start with \"-l\""); + println!("cargo::rustc-link-lib=dylib={link_lib}"); + } + + (llvm_definitions.to_string(), llvm_include_dirs.to_string()) +} + +/// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs +fn compile_cpp_dependencies(genmc_path: &Path) { + // Part 1: + // Compile the GenMC library using cmake. + + let cmakelists_path = genmc_path.join("CMakeLists.txt"); + + // FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed. + // Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release) + let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug"); + + let mut config = cmake::Config::new(cmakelists_path); + config.profile(GENMC_CMAKE_PROFILE); + config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" }); + + // The actual compilation happens here: + let genmc_install_dir = config.build(); + + // Add the model checker library to be linked and tell GenMC where to find it: + let cmake_lib_dir = genmc_install_dir.join("lib").join("genmc"); + println!("cargo::rustc-link-search=native={}", cmake_lib_dir.display()); + println!("cargo::rustc-link-lib=static={GENMC_MODEL_CHECKER}"); + + // FIXME(genmc,llvm): Remove once the LLVM dependency of the GenMC model checker is removed. + let config_file = genmc_install_dir.join("include").join("genmc").join("config.h"); + let (llvm_definitions, llvm_include_dirs) = link_to_llvm(&config_file); + + // Part 2: + // Compile the cxx_bridge (the link between the Rust and C++ code). + + let genmc_include_dir = genmc_install_dir.join("include").join("genmc"); + + // FIXME(genmc,llvm): remove once LLVM dependency is removed. + // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated. + let definitions = llvm_definitions.split(";"); + + let mut bridge = cxx_build::bridge("src/lib.rs"); + // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file. + if enable_genmc_debug { + bridge.define("ENABLE_GENMC_DEBUG", None); + } + bridge + .flags(definitions) + .opt_level(2) + .debug(true) // Same settings that GenMC uses (default for cmake `RelWithDebInfo`) + .warnings(false) // NOTE: enabling this produces a lot of warnings. + .std("c++23") + .include(genmc_include_dir) + .include(llvm_include_dirs) + .include("./src_cpp") + .file("./src_cpp/MiriInterface.hpp") + .file("./src_cpp/MiriInterface.cpp") + .compile("genmc_interop"); + + // Link the Rust-C++ interface library generated by cxx_build: + println!("cargo::rustc-link-lib=static=genmc_interop"); +} + +fn main() { + // Make sure we don't accidentally distribute a binary with GPL code. + if option_env!("RUSTC_STAGE").is_some() { + panic!( + "genmc should not be enabled in the rustc workspace since it includes a GPL dependency" + ); + } + + // Select which path to use for the GenMC repo: + let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") { + let genmc_src_path = + PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path"); + assert!( + genmc_src_path.exists(), + "GENMC_SRC_PATH={} does not exist!", + genmc_src_path.display() + ); + genmc_src_path + } else { + downloading::download_genmc() + }; + + // Build all required components: + compile_cpp_dependencies(&genmc_path); + + // Only rebuild if anything changes: + // Note that we don't add the downloaded GenMC repo, since that should never be modified manually. + // Adding that path here would also trigger an unnecessary rebuild after the repo is cloned (since cargo detects that as a file modification). + println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}"); + println!("cargo::rerun-if-changed=./src_cpp"); + println!("cargo::rerun-if-env-changed=GENMC_SRC_PATH"); +} diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs new file mode 100644 index 000000000000..ab46d729ea16 --- /dev/null +++ b/src/tools/miri/genmc-sys/src/lib.rs @@ -0,0 +1,30 @@ +pub use self::ffi::*; + +impl Default for GenmcParams { + fn default() -> Self { + Self { + print_random_schedule_seed: false, + do_symmetry_reduction: false, + // FIXME(GenMC): Add defaults for remaining parameters + } + } +} + +#[cxx::bridge] +mod ffi { + /// Parameters that will be given to GenMC for setting up the model checker. + /// (The fields of this struct are visible to both Rust and C++) + #[derive(Clone, Debug)] + struct GenmcParams { + pub print_random_schedule_seed: bool, + pub do_symmetry_reduction: bool, + // FIXME(GenMC): Add remaining parameters. + } + unsafe extern "C++" { + include!("MiriInterface.hpp"); + + type MiriGenMCShim; + + fn createGenmcHandle(config: &GenmcParams) -> UniquePtr; + } +} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp new file mode 100644 index 000000000000..0827bb3d4074 --- /dev/null +++ b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp @@ -0,0 +1,50 @@ +#include "MiriInterface.hpp" + +#include "genmc-sys/src/lib.rs.h" + +auto MiriGenMCShim::createHandle(const GenmcParams &config) + -> std::unique_ptr +{ + auto conf = std::make_shared(); + + // Miri needs all threads to be replayed, even fully completed ones. + conf->replayCompletedThreads = true; + + // We only support the RC11 memory model for Rust. + conf->model = ModelType::RC11; + + conf->printRandomScheduleSeed = config.print_random_schedule_seed; + + // FIXME(genmc): disable any options we don't support currently: + conf->ipr = false; + conf->disableBAM = true; + conf->instructionCaching = false; + + ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode."); + conf->symmetryReduction = config.do_symmetry_reduction; + + // FIXME(genmc): Should there be a way to change this option from Miri? + conf->schedulePolicy = SchedulePolicy::WF; + + // FIXME(genmc): implement estimation mode: + conf->estimate = false; + conf->estimationMax = 1000; + const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{}) + : GenMCDriver::Mode(GenMCDriver::VerificationMode{}); + + // Running Miri-GenMC without race detection is not supported. + // Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri. + // This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option, + // the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`). + conf->disableRaceDetection = false; + + // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory + // that is allowed to leak and memory that is not. + conf->warnUnfreedMemory = false; + + // FIXME(genmc): check config: + // checkConfigOptions(*conf); + + auto driver = std::make_unique(std::move(conf), mode); + return driver; +} diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp new file mode 100644 index 000000000000..e55522ef4189 --- /dev/null +++ b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp @@ -0,0 +1,44 @@ +#ifndef GENMC_MIRI_INTERFACE_HPP +#define GENMC_MIRI_INTERFACE_HPP + +#include "rust/cxx.h" + +#include "config.h" + +#include "Config/Config.hpp" +#include "Verification/GenMCDriver.hpp" + +#include + +/**** Types available to Miri ****/ + +// Config struct defined on the Rust side and translated to C++ by cxx.rs: +struct GenmcParams; + +struct MiriGenMCShim : private GenMCDriver +{ + +public: + MiriGenMCShim(std::shared_ptr conf, Mode mode /* = VerificationMode{} */) + : GenMCDriver(std::move(conf), nullptr, mode) + { + std::cerr << "C++: GenMC handle created!" << std::endl; + } + + virtual ~MiriGenMCShim() + { + std::cerr << "C++: GenMC handle destroyed!" << std::endl; + } + + static std::unique_ptr createHandle(const GenmcParams &config); +}; + +/**** Functions available to Miri ****/ + +// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead. +static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr +{ + return MiriGenMCShim::createHandle(config); +} + +#endif /* GENMC_MIRI_INTERFACE_HPP */ diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index 9aaad9ca04a9..7f3bc1bc757d 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -757,8 +757,8 @@ impl Command { if ty.is_file() { name.ends_with(".rs") } else { - // dir or symlink. skip `target` and `.git`. - &name != "target" && &name != ".git" + // dir or symlink. skip `target`, `.git` and `genmc-src*` + &name != "target" && &name != ".git" && !name.starts_with("genmc-src") } }) .filter_ok(|item| item.file_type().is_file()) diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 89fa980ff646..11d17994cc22 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -67,8 +67,6 @@ use crate::log::setup::{deinit_loggers, init_early_loggers, init_late_loggers}; struct MiriCompilerCalls { miri_config: Option, many_seeds: Option, - /// Settings for using GenMC with Miri. - genmc_config: Option, } struct ManySeedsConfig { @@ -77,12 +75,8 @@ struct ManySeedsConfig { } impl MiriCompilerCalls { - fn new( - miri_config: MiriConfig, - many_seeds: Option, - genmc_config: Option, - ) -> Self { - Self { miri_config: Some(miri_config), many_seeds, genmc_config } + fn new(miri_config: MiriConfig, many_seeds: Option) -> Self { + Self { miri_config: Some(miri_config), many_seeds } } } @@ -192,8 +186,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { optimizations is usually marginal at best."); } - if let Some(genmc_config) = &self.genmc_config { - let _genmc_ctx = Rc::new(GenmcCtx::new(&config, genmc_config)); + if let Some(_genmc_config) = &config.genmc_config { + let _genmc_ctx = Rc::new(GenmcCtx::new(&config)); todo!("GenMC mode not yet implemented"); }; @@ -487,7 +481,6 @@ fn main() { let mut many_seeds_keep_going = false; let mut miri_config = MiriConfig::default(); miri_config.env = env_snapshot; - let mut genmc_config = None; let mut rustc_args = vec![]; let mut after_dashdash = false; @@ -603,9 +596,7 @@ fn main() { } else if arg == "-Zmiri-many-seeds-keep-going" { many_seeds_keep_going = true; } else if let Some(trimmed_arg) = arg.strip_prefix("-Zmiri-genmc") { - // FIXME(GenMC): Currently, GenMC mode is incompatible with aliasing model checking. - miri_config.borrow_tracker = None; - GenmcConfig::parse_arg(&mut genmc_config, trimmed_arg); + GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg); } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") { @@ -740,13 +731,21 @@ fn main() { many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); // Validate settings for data race detection and GenMC mode. - assert_eq!(genmc_config.is_some(), miri_config.genmc_mode); - if genmc_config.is_some() { + if miri_config.genmc_config.is_some() { + // FIXME(genmc): Add checks for currently supported platforms (64bit, target == host) if !miri_config.data_race_detector { fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); } + // FIXME(genmc): Remove once GenMC mode is compatible with borrow tracking: + if miri_config.borrow_tracker.is_some() { + eprintln!( + "warning: Borrow tracking has been disabled, it is not (yet) supported in GenMC mode." + ); + eprintln!(); + miri_config.borrow_tracker = None; + } } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { fatal_error!( "Weak memory emulation cannot be enabled when the data race detector is disabled" @@ -765,8 +764,5 @@ fn main() { ); } } - run_compiler_and_exit( - &rustc_args, - &mut MiriCompilerCalls::new(miri_config, many_seeds, genmc_config), - ) + run_compiler_and_exit(&rustc_args, &mut MiriCompilerCalls::new(miri_config, many_seeds)) } diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index f91211a670f6..42591c9ac256 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -1,19 +1,41 @@ -use crate::MiriConfig; +use super::GenmcParams; +/// Configuration for GenMC mode. +/// The `params` field is shared with the C++ side. +/// The remaining options are kept on the Rust side. #[derive(Debug, Default, Clone)] pub struct GenmcConfig { - // TODO: add fields + pub(super) params: GenmcParams, + do_estimation: bool, + // FIXME(GenMC): add remaining options. } impl GenmcConfig { /// Function for parsing command line options for GenMC mode. - /// All GenMC arguments start with the string "-Zmiri-genmc". /// - /// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed + /// All GenMC arguments start with the string "-Zmiri-genmc". + /// Passing any GenMC argument will enable GenMC mode. + /// + /// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed. pub fn parse_arg(genmc_config: &mut Option, trimmed_arg: &str) { + // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. + if !cfg!(all( + feature = "genmc", + any(target_os = "linux", target_os = "macos"), + target_pointer_width = "64", + target_endian = "little" + )) { + unimplemented!( + "GenMC mode is not supported on this platform, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" + ); + } if genmc_config.is_none() { *genmc_config = Some(Default::default()); } - todo!("implement parsing of GenMC options") + if trimmed_arg.is_empty() { + return; // this corresponds to "-Zmiri-genmc" + } + // FIXME(GenMC): implement remaining parameters. + todo!(); } } diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 3d0558fb6853..7570f8ba3627 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -16,7 +16,7 @@ pub struct GenmcCtx {} pub struct GenmcConfig {} impl GenmcCtx { - pub fn new(_miri_config: &MiriConfig, _genmc_config: &GenmcConfig) -> Self { + pub fn new(_miri_config: &MiriConfig) -> Self { unreachable!() } @@ -228,9 +228,21 @@ impl VisitProvenance for GenmcCtx { impl GenmcConfig { pub fn parse_arg(_genmc_config: &mut Option, trimmed_arg: &str) { - unimplemented!( - "GenMC feature im Miri is disabled, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" - ); + // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. + if cfg!(all( + feature = "genmc", + any(target_os = "linux", target_os = "macos"), + target_pointer_width = "64", + target_endian = "little" + )) { + unimplemented!( + "GenMC is disabled, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" + ); + } else { + unimplemented!( + "GenMC mode is not supported on this platform, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" + ); + } } pub fn should_print_graph(&self, _rep: usize) -> bool { diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs index 0dfd4b9b80f9..3617775e27ea 100644 --- a/src/tools/miri/src/concurrency/genmc/mod.rs +++ b/src/tools/miri/src/concurrency/genmc/mod.rs @@ -2,6 +2,7 @@ use std::cell::Cell; +use genmc_sys::{GenmcParams, createGenmcHandle}; use rustc_abi::{Align, Size}; use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok}; use rustc_middle::mir; @@ -24,9 +25,19 @@ pub struct GenmcCtx { impl GenmcCtx { /// Create a new `GenmcCtx` from a given config. - pub fn new(miri_config: &MiriConfig, genmc_config: &GenmcConfig) -> Self { - assert!(miri_config.genmc_mode); - todo!() + pub fn new(miri_config: &MiriConfig) -> Self { + let genmc_config = miri_config.genmc_config.as_ref().unwrap(); + + let handle = createGenmcHandle(&genmc_config.params); + assert!(!handle.is_null()); + + eprintln!("Miri: GenMC handle creation successful!"); + + drop(handle); + eprintln!("Miri: Dropping GenMC handle successful!"); + + // FIXME(GenMC): implement + std::process::exit(0); } pub fn get_stuck_execution_count(&self) -> usize { diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs index c2ea8a00decd..435615efd9fa 100644 --- a/src/tools/miri/src/concurrency/mod.rs +++ b/src/tools/miri/src/concurrency/mod.rs @@ -8,7 +8,17 @@ mod vector_clock; pub mod weak_memory; // Import either the real genmc adapter or a dummy module. -#[cfg_attr(not(feature = "genmc"), path = "genmc/dummy.rs")] +// On unsupported platforms, we always include the dummy module, even if the `genmc` feature is enabled. +// FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. +#[cfg_attr( + not(all( + feature = "genmc", + target_os = "linux", + target_pointer_width = "64", + target_endian = "little" + )), + path = "genmc/dummy.rs" +)] mod genmc; pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler}; diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 878afdf25173..abfee0ee8746 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -677,8 +677,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling. - // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we - // support pre-main constructors, it can get called there as well. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { let thread_id = this.active_thread(); genmc_ctx.handle_thread_stack_empty(thread_id); diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 3c80e60b772a..8a20b7fab557 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -125,8 +125,8 @@ pub struct MiriConfig { pub data_race_detector: bool, /// Determine if weak memory emulation should be enabled. Requires data race detection to be enabled. pub weak_memory_emulation: bool, - /// Determine if we are running in GenMC mode. In this mode, Miri will explore multiple concurrent executions of the given program. - pub genmc_mode: bool, + /// Determine if we are running in GenMC mode and with which settings. In GenMC mode, Miri will explore multiple concurrent executions of the given program. + pub genmc_config: Option, /// Track when an outdated (weak memory) load happens. pub track_outdated_loads: bool, /// Rate of spurious failures for compare_exchange_weak atomic operations, @@ -192,7 +192,7 @@ impl Default for MiriConfig { track_alloc_accesses: false, data_race_detector: true, weak_memory_emulation: true, - genmc_mode: false, + genmc_config: None, track_outdated_loads: false, cmpxchg_weak_failure_rate: 0.8, // 80% measureme_out: None, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index ce33c870b4b5..8b7a8a5865a9 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -622,6 +622,9 @@ pub struct MiriMachine<'tcx> { } impl<'tcx> MiriMachine<'tcx> { + /// Create a new MiriMachine. + /// + /// Invariant: `genmc_ctx.is_some() == config.genmc_config.is_some()` pub(crate) fn new( config: &MiriConfig, layout_cx: LayoutCx<'tcx>, @@ -645,7 +648,7 @@ impl<'tcx> MiriMachine<'tcx> { }); let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0)); let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config)); - let data_race = if config.genmc_mode { + let data_race = if config.genmc_config.is_some() { // `genmc_ctx` persists across executions, so we don't create a new one here. GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap()) } else if config.data_race_detector { diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs new file mode 100644 index 000000000000..f621bd9114fa --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs @@ -0,0 +1,8 @@ +//@compile-flags: -Zmiri-genmc + +#![no_main] + +#[unsafe(no_mangle)] +fn miri_start(_argc: isize, _argv: *const *const u8) -> isize { + 0 +} diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr new file mode 100644 index 000000000000..3773dbeff3b4 --- /dev/null +++ b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr @@ -0,0 +1,6 @@ +warning: Borrow tracking has been disabled, it is not (yet) supported in GenMC mode. + +C++: GenMC handle created! +Miri: GenMC handle creation successful! +C++: GenMC handle destroyed! +Miri: Dropping GenMC handle successful! diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index cb915b11b679..73fbe2cc020f 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -338,6 +338,20 @@ fn main() -> Result<()> { ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?; } + // We only enable GenMC tests when the `genmc` feature is enabled, but also only on platforms we support: + // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. + // FIXME(genmc,cross-platform): remove `host == target` check once cross-platform support with GenMC is possible. + if cfg!(all( + feature = "genmc", + target_os = "linux", + target_pointer_width = "64", + target_endian = "little" + )) && host == target + { + ui(Mode::Pass, "tests/genmc/pass", &target, WithDependencies, tmpdir.path())?; + ui(Mode::Fail, "tests/genmc/fail", &target, WithDependencies, tmpdir.path())?; + } + Ok(()) } From 87b8cb06c793620f1f66291200aea1b9bd8c3d89 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 16:34:42 +0200 Subject: [PATCH 096/201] various minor adjustments --- src/tools/miri/doc/genmc.md | 52 +++++++------------ src/tools/miri/genmc-sys/build.rs | 17 +++--- src/tools/miri/src/bin/miri.rs | 9 ++-- .../miri/src/concurrency/genmc/config.rs | 20 +++---- src/tools/miri/src/concurrency/genmc/dummy.rs | 21 +++----- src/tools/miri/src/concurrency/thread.rs | 2 + .../tests/genmc/pass/test_cxx_build.stderr | 3 +- 7 files changed, 47 insertions(+), 77 deletions(-) diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md index 4839919557ec..5aabe90b5dab 100644 --- a/src/tools/miri/doc/genmc.md +++ b/src/tools/miri/doc/genmc.md @@ -1,5 +1,7 @@ # **(WIP)** Documentation for Miri-GenMC + [GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program. +Miri-GenMC integrates that model checker into Miri. **NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.** @@ -7,9 +9,7 @@ ## Usage -**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", which is NOT compatible with Miri's "MIT OR Apache" license.** - -**IMPORTANT: There should be no distribution of Miri-GenMC until all licensing questions are clarified (the `genmc` feature of Miri is OFF-BY-DEFAULT and should be OFF for all Miri releases).** +**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.** For testing/developing Miri-GenMC (while keeping in mind the licensing issues): - clone the Miri repo. @@ -35,44 +35,28 @@ Some or all of these limitations might get removed in the future: - Borrow tracking is currently incompatible (stacked/tree borrows). - Only Linux is supported for now. -- No 32-bit platform support. -- No cross-platform interpretation. +- No support for 32-bit or big-endian targets. +- No cross-target interpretation. ## Development GenMC is written in C++, which complicates development a bit. -For Rust-C++ interop, Miri uses [CXX.rs](https://cxx.rs/), and all handling of C++ code is contained in the `genmc-sys` crate (located in the Miri repository root directory: `miri/genmc-sys/`). +The prerequisites for building Miri-GenMC are: +- A compiler with C++23 support. +- LLVM developments headers and clang. + -Building GenMC requires a compiler with C++23 support. - -Currently, building GenMC also requires linking to LLVM, which needs to be installed manually. +The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers). +These sources need to be available to build Miri-GenMC. +The process for obtaining them is as follows: +- By default, a fixed commit of GenMC is downloaded to `genmc-sys/genmc-src` and built automatically. + (The commit is determined by `GENMC_COMMIT` in `genmc-sys/build.rs`.) +- If you want to overwrite that, set the `GENMC_SRC_PATH` environment variable to a path that contains the GenMC sources. + If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid + formatting the Rust files inside that folder. -The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with different maintainers). -Note that this repo is just a mirror repo. - + - -### Building the GenMC Library -The build script in the `genmc-sys` crate handles locating, downloading, building and linking the GenMC library. - -To determine which GenMC repo path will be used, the following steps are taken: -- If the env var `GENMC_SRC_PATH` is set, it's value is used as a path to a directory with a GenMC repo (e.g., `GENMC_SRC_PATH="path/to/miri/genmc-sys/genmc-sys-local"`). - - Note that this variable must be set wherever Miri is built, e.g., in the terminal, or in the Rust Analyzer settings. -- If the path `genmc-sys/genmc-src/genmc` exists, try to set the GenMC repo there to the commit we need. -- If the downloaded repo doesn't exist or is missing the commit, the build script will fetch the commit over the network. - - Note that the build script will *not* access the network if any of the steps previous steps succeeds. - -Once we get the path to the repo, the compilation proceeds in two steps: -- Compile GenMC into a library (using cmake). -- Compile the cxx.rs bridge to connect the library to the Rust code. -The first step is where all build settings are made, the relevant ones are then stored in a `config.h` file that can be included in the second compilation step. - -#### Code Formatting -Note that all directories with names starting with `genmc-src` are ignored by `./miri fmt` on purpose. -GenMC also contains Rust files, but they should not be formatted with Miri's formatting rules. -For working on Miri-GenMC locally, placing the GenMC repo into such a path (e.g., `miri/genmc-sys/genmc-src-local`) ensures that it is also exempt from formatting. - - diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index 16f7b69cc211..f20e0e8d525f 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -6,7 +6,7 @@ use std::str::FromStr; /// Path where the downloaded GenMC repository will be stored (relative to the `genmc-sys` directory). /// Note that this directory is *not* cleaned up automatically by `cargo clean`. -const GENMC_DOWNLOAD_PATH: &str = "./genmc-src/genmc/"; +const GENMC_DOWNLOAD_PATH: &str = "./genmc-src/"; /// Name of the library of the GenMC model checker. const GENMC_MODEL_CHECKER: &str = "genmc_lib"; @@ -122,11 +122,9 @@ mod downloading { return; } - println!( - "HINT: For local development, set the environment variable 'GENMC_SRC_PATH' to the path of a GenMC repository.", - ); panic!( - "Downloaded GenMC repository at path '{GENMC_DOWNLOAD_PATH}' has been modified. Please undo any changes made, or delete the '{GENMC_DOWNLOAD_PATH}' directory to have it downloaded again." + "Downloaded GenMC repository at path '{GENMC_DOWNLOAD_PATH}' has been modified. Please undo any changes made, or delete the '{GENMC_DOWNLOAD_PATH}' directory to have it downloaded again.\n\ + HINT: For local development, set the environment variable 'GENMC_SRC_PATH' to the path of a GenMC repository." ); } } @@ -194,7 +192,7 @@ fn compile_cpp_dependencies(genmc_path: &Path) { // The actual compilation happens here: let genmc_install_dir = config.build(); - // Add the model checker library to be linked and tell GenMC where to find it: + // Add the model checker library to be linked and tell rustc where to find it: let cmake_lib_dir = genmc_install_dir.join("lib").join("genmc"); println!("cargo::rustc-link-search=native={}", cmake_lib_dir.display()); println!("cargo::rustc-link-lib=static={GENMC_MODEL_CHECKER}"); @@ -260,9 +258,10 @@ fn main() { compile_cpp_dependencies(&genmc_path); // Only rebuild if anything changes: - // Note that we don't add the downloaded GenMC repo, since that should never be modified manually. - // Adding that path here would also trigger an unnecessary rebuild after the repo is cloned (since cargo detects that as a file modification). + // Note that we don't add the downloaded GenMC repo, since that should never be modified + // manually. Adding that path here would also trigger an unnecessary rebuild after the repo is + // cloned (since cargo detects that as a file modification). println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}"); + println!("cargo::rerun-if-changed=./src"); println!("cargo::rerun-if-changed=./src_cpp"); - println!("cargo::rerun-if-env-changed=GENMC_SRC_PATH"); } diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 11d17994cc22..ae1b25f8857a 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -596,7 +596,9 @@ fn main() { } else if arg == "-Zmiri-many-seeds-keep-going" { many_seeds_keep_going = true; } else if let Some(trimmed_arg) = arg.strip_prefix("-Zmiri-genmc") { - GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg); + if let Err(msg) = GenmcConfig::parse_arg(&mut miri_config.genmc_config, trimmed_arg) { + fatal_error!("{msg}"); + } } else if let Some(param) = arg.strip_prefix("-Zmiri-env-forward=") { miri_config.forwarded_env_vars.push(param.to_owned()); } else if let Some(param) = arg.strip_prefix("-Zmiri-env-set=") { @@ -732,18 +734,15 @@ fn main() { // Validate settings for data race detection and GenMC mode. if miri_config.genmc_config.is_some() { - // FIXME(genmc): Add checks for currently supported platforms (64bit, target == host) if !miri_config.data_race_detector { fatal_error!("Cannot disable data race detection in GenMC mode (currently)"); } else if !miri_config.weak_memory_emulation { fatal_error!("Cannot disable weak memory emulation in GenMC mode"); } - // FIXME(genmc): Remove once GenMC mode is compatible with borrow tracking: if miri_config.borrow_tracker.is_some() { eprintln!( - "warning: Borrow tracking has been disabled, it is not (yet) supported in GenMC mode." + "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode." ); - eprintln!(); miri_config.borrow_tracker = None; } } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector { diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs index 42591c9ac256..c56adab90fe2 100644 --- a/src/tools/miri/src/concurrency/genmc/config.rs +++ b/src/tools/miri/src/concurrency/genmc/config.rs @@ -17,23 +17,17 @@ impl GenmcConfig { /// Passing any GenMC argument will enable GenMC mode. /// /// `trimmed_arg` should be the argument to be parsed, with the suffix "-Zmiri-genmc" removed. - pub fn parse_arg(genmc_config: &mut Option, trimmed_arg: &str) { - // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. - if !cfg!(all( - feature = "genmc", - any(target_os = "linux", target_os = "macos"), - target_pointer_width = "64", - target_endian = "little" - )) { - unimplemented!( - "GenMC mode is not supported on this platform, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" - ); - } + pub fn parse_arg( + genmc_config: &mut Option, + trimmed_arg: &str, + ) -> Result<(), String> { + // FIXME(genmc): Ensure host == target somewhere. + if genmc_config.is_none() { *genmc_config = Some(Default::default()); } if trimmed_arg.is_empty() { - return; // this corresponds to "-Zmiri-genmc" + return Ok(()); // this corresponds to "-Zmiri-genmc" } // FIXME(GenMC): implement remaining parameters. todo!(); diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs index 7570f8ba3627..79d27c4be159 100644 --- a/src/tools/miri/src/concurrency/genmc/dummy.rs +++ b/src/tools/miri/src/concurrency/genmc/dummy.rs @@ -227,21 +227,14 @@ impl VisitProvenance for GenmcCtx { } impl GenmcConfig { - pub fn parse_arg(_genmc_config: &mut Option, trimmed_arg: &str) { - // FIXME(genmc,macos): Add `target_os = "macos"` once `https://github.com/dtolnay/cxx/issues/1535` is fixed. - if cfg!(all( - feature = "genmc", - any(target_os = "linux", target_os = "macos"), - target_pointer_width = "64", - target_endian = "little" - )) { - unimplemented!( - "GenMC is disabled, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" - ); + pub fn parse_arg( + _genmc_config: &mut Option, + trimmed_arg: &str, + ) -> Result<(), String> { + if cfg!(feature = "genmc") { + Err(format!("GenMC is disabled in this build of Miri")) } else { - unimplemented!( - "GenMC mode is not supported on this platform, cannot handle argument: \"-Zmiri-genmc{trimmed_arg}\"" - ); + Err(format!("GenMC is not supported on this target")) } } diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index abfee0ee8746..878afdf25173 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -677,6 +677,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> { let this = self.eval_context_mut(); // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling. + // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we + // support pre-main constructors, it can get called there as well. if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() { let thread_id = this.active_thread(); genmc_ctx.handle_thread_stack_empty(thread_id); diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr index 3773dbeff3b4..4b7aa824bd12 100644 --- a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr +++ b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr @@ -1,5 +1,4 @@ -warning: Borrow tracking has been disabled, it is not (yet) supported in GenMC mode. - +warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode. C++: GenMC handle created! Miri: GenMC handle creation successful! C++: GenMC handle destroyed! From d12ecde365efcf6ee4eb74fb6cfdd8314ab9fec4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 17:00:31 +0200 Subject: [PATCH 097/201] constify with_exposed_provenance --- library/core/src/ptr/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index dbe3999b4a43..1a2a5182567b 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -974,9 +974,10 @@ pub const fn dangling_mut() -> *mut T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] +#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn with_exposed_provenance(addr: usize) -> *const T { +pub const fn with_exposed_provenance(addr: usize) -> *const T { addr as *const T } @@ -1014,9 +1015,10 @@ pub fn with_exposed_provenance(addr: usize) -> *const T { #[must_use] #[inline(always)] #[stable(feature = "exposed_provenance", since = "1.84.0")] +#[rustc_const_unstable(feature = "const_exposed_provenance", issue = "144538")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces #[allow(fuzzy_provenance_casts)] // this *is* the explicit provenance API one should use instead -pub fn with_exposed_provenance_mut(addr: usize) -> *mut T { +pub const fn with_exposed_provenance_mut(addr: usize) -> *mut T { addr as *mut T } From 26041011228c63550d90e49408502dd5be3d1498 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 17:06:03 +0200 Subject: [PATCH 098/201] internal: Better type proc macro dylib build data state --- .../rust-analyzer/crates/base-db/src/input.rs | 25 +++- .../project-model/src/build_dependencies.rs | 115 ++++++++++++------ .../project-model/src/cargo_workspace.rs | 14 +-- .../crates/project-model/src/lib.rs | 19 +-- .../crates/project-model/src/sysroot.rs | 50 ++++---- .../crates/project-model/src/workspace.rs | 66 +++++----- 6 files changed, 175 insertions(+), 114 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 8c9393bcc93a..fdaebeaa934e 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -30,7 +30,8 @@ pub type ProcMacroPaths = pub enum ProcMacroLoadingError { Disabled, FailedToBuild, - MissingDylibPath, + ExpectedProcMacroArtifact, + MissingDylibPath(Box<[String]>), NotYetBuilt, NoProcMacros, ProcMacroSrvError(Box), @@ -39,8 +40,9 @@ impl ProcMacroLoadingError { pub fn is_hard_error(&self) -> bool { match self { ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false, - ProcMacroLoadingError::FailedToBuild - | ProcMacroLoadingError::MissingDylibPath + ProcMacroLoadingError::ExpectedProcMacroArtifact + | ProcMacroLoadingError::FailedToBuild + | ProcMacroLoadingError::MissingDylibPath(_) | ProcMacroLoadingError::NoProcMacros | ProcMacroLoadingError::ProcMacroSrvError(_) => true, } @@ -51,10 +53,23 @@ impl Error for ProcMacroLoadingError {} impl fmt::Display for ProcMacroLoadingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + ProcMacroLoadingError::ExpectedProcMacroArtifact => { + write!(f, "proc-macro crate did not build proc-macro artifact") + } ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"), ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"), - ProcMacroLoadingError::MissingDylibPath => { - write!(f, "proc-macro crate build data is missing a dylib path") + ProcMacroLoadingError::MissingDylibPath(candidates) if candidates.is_empty() => { + write!( + f, + "proc-macro crate built but the dylib path is missing, this indicates a problem with your build system." + ) + } + ProcMacroLoadingError::MissingDylibPath(candidates) => { + write!( + f, + "proc-macro crate built but the dylib path is missing, this indicates a problem with your build system. Candidates not considered due to not having a dynamic library extension: {}", + candidates.join(", ") + ) } ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"), ProcMacroLoadingError::NoProcMacros => { diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 499caa622c4d..664665625f99 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -16,6 +16,7 @@ use la_arena::ArenaMap; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize as _; +use stdx::{always, never}; use toolchain::Tool; use crate::{ @@ -30,6 +31,15 @@ pub struct WorkspaceBuildScripts { error: Option, } +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub enum ProcMacroDylibPath { + Path(AbsPathBuf), + DylibNotFound(Box<[Utf8PathBuf]>), + NotProcMacro, + #[default] + NotBuilt, +} + /// Output of the build script and proc-macro building step for a concrete package. #[derive(Debug, Clone, Default, PartialEq, Eq)] pub(crate) struct BuildScriptOutput { @@ -43,7 +53,7 @@ pub(crate) struct BuildScriptOutput { /// Directory where a build script might place its output. pub(crate) out_dir: Option, /// Path to the proc-macro library file if this package exposes proc-macros. - pub(crate) proc_macro_dylib_path: Option, + pub(crate) proc_macro_dylib_path: ProcMacroDylibPath, } impl BuildScriptOutput { @@ -51,7 +61,7 @@ impl BuildScriptOutput { self.cfgs.is_empty() && self.envs.is_empty() && self.out_dir.is_none() - && self.proc_macro_dylib_path.is_none() + && self.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt } } @@ -126,6 +136,8 @@ impl WorkspaceBuildScripts { |package, cb| { if let Some(&(package, workspace)) = by_id.get(package) { cb(&workspaces[workspace][package].name, &mut res[workspace].outputs[package]); + } else { + never!("Received compiler message for unknown package: {}", package); } }, progress, @@ -140,12 +152,9 @@ impl WorkspaceBuildScripts { if tracing::enabled!(tracing::Level::INFO) { for (idx, workspace) in workspaces.iter().enumerate() { for package in workspace.packages() { - let package_build_data = &mut res[idx].outputs[package]; + let package_build_data: &mut BuildScriptOutput = &mut res[idx].outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - workspace[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,); } } } @@ -198,10 +207,33 @@ impl WorkspaceBuildScripts { let path = dir_entry.path(); let extension = path.extension()?; if extension == std::env::consts::DLL_EXTENSION { - let name = path.file_stem()?.to_str()?.split_once('-')?.0.to_owned(); - let path = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).ok()?) - .ok()?; - return Some((name, path)); + let name = path + .file_stem()? + .to_str()? + .split_once('-')? + .0 + .trim_start_matches("lib") + .to_owned(); + let path = match Utf8PathBuf::from_path_buf(path) { + Ok(path) => path, + Err(path) => { + tracing::warn!( + "Proc-macro dylib path contains non-UTF8 characters: {:?}", + path.display() + ); + return None; + } + }; + return match AbsPathBuf::try_from(path) { + Ok(path) => Some((name, path)), + Err(path) => { + tracing::error!( + "proc-macro dylib path is not absolute: {:?}", + path + ); + None + } + }; } } None @@ -209,28 +241,24 @@ impl WorkspaceBuildScripts { .collect(); for p in rustc.packages() { let package = &rustc[p]; - if package - .targets - .iter() - .any(|&it| matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true })) - { - if let Some((_, path)) = proc_macro_dylibs - .iter() - .find(|(name, _)| *name.trim_start_matches("lib") == package.name) - { - bs.outputs[p].proc_macro_dylib_path = Some(path.clone()); + bs.outputs[p].proc_macro_dylib_path = + if package.targets.iter().any(|&it| { + matches!(rustc[it].kind, TargetKind::Lib { is_proc_macro: true }) + }) { + match proc_macro_dylibs.iter().find(|(name, _)| *name == package.name) { + Some((_, path)) => ProcMacroDylibPath::Path(path.clone()), + _ => ProcMacroDylibPath::DylibNotFound(Box::default()), + } + } else { + ProcMacroDylibPath::NotProcMacro } - } } if tracing::enabled!(tracing::Level::INFO) { for package in rustc.packages() { let package_build_data = &bs.outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - rustc[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", rustc[package].manifest,); } } } @@ -263,6 +291,12 @@ impl WorkspaceBuildScripts { |package, cb| { if let Some(&package) = by_id.get(package) { cb(&workspace[package].name, &mut outputs[package]); + } else { + never!( + "Received compiler message for unknown package: {}\n {}", + package, + by_id.keys().join(", ") + ); } }, progress, @@ -272,10 +306,7 @@ impl WorkspaceBuildScripts { for package in workspace.packages() { let package_build_data = &outputs[package]; if !package_build_data.is_empty() { - tracing::info!( - "{}: {package_build_data:?}", - workspace[package].manifest.parent(), - ); + tracing::info!("{}: {package_build_data:?}", workspace[package].manifest,); } } } @@ -348,15 +379,25 @@ impl WorkspaceBuildScripts { progress(format!( "building compile-time-deps: proc-macro {name} built" )); + always!( + data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt, + "received multiple compiler artifacts for the same package: {message:?}" + ); + if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { + data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; + } if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) { - // Skip rmeta file - if let Some(filename) = - message.filenames.iter().find(|file| is_dylib(file)) - { - let filename = AbsPath::assert(filename); - data.proc_macro_dylib_path = Some(filename.to_owned()); - } + data.proc_macro_dylib_path = + match message.filenames.iter().find(|file| is_dylib(file)) { + Some(filename) => { + let filename = AbsPath::assert(filename); + ProcMacroDylibPath::Path(filename.to_owned()) + } + None => ProcMacroDylibPath::DylibNotFound( + message.filenames.clone().into_boxed_slice(), + ), + }; } }); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 25abb19d9d55..767a4aade09a 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -245,7 +245,7 @@ pub enum TargetKind { } impl TargetKind { - fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind { + pub fn new(kinds: &[cargo_metadata::TargetKind]) -> TargetKind { for kind in kinds { return match kind { cargo_metadata::TargetKind::Bin => TargetKind::Bin, @@ -604,12 +604,12 @@ impl FetchMetadata { // but nothing else let mut extra_args = config.extra_args.iter(); while let Some(arg) = extra_args.next() { - if arg == "-Z" { - if let Some(arg) = extra_args.next() { - needs_nightly = true; - other_options.push("-Z".to_owned()); - other_options.push(arg.to_owned()); - } + if arg == "-Z" + && let Some(arg) = extra_args.next() + { + needs_nightly = true; + other_options.push("-Z".to_owned()); + other_options.push(arg.to_owned()); } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index 3bf3d06e6b1a..d39781b15066 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -59,7 +59,7 @@ use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::FxHashSet; pub use crate::{ - build_dependencies::WorkspaceBuildScripts, + build_dependencies::{ProcMacroDylibPath, WorkspaceBuildScripts}, cargo_workspace::{ CargoConfig, CargoFeatures, CargoMetadataConfig, CargoWorkspace, Package, PackageData, PackageDependency, RustLibSource, Target, TargetData, TargetKind, @@ -139,21 +139,22 @@ impl ProjectManifest { } fn find_in_parent_dirs(path: &AbsPath, target_file_name: &str) -> Option { - if path.file_name().unwrap_or_default() == target_file_name { - if let Ok(manifest) = ManifestPath::try_from(path.to_path_buf()) { - return Some(manifest); - } + if path.file_name().unwrap_or_default() == target_file_name + && let Ok(manifest) = ManifestPath::try_from(path.to_path_buf()) + { + return Some(manifest); } let mut curr = Some(path); while let Some(path) = curr { let candidate = path.join(target_file_name); - if fs::metadata(&candidate).is_ok() { - if let Ok(manifest) = ManifestPath::try_from(candidate) { - return Some(manifest); - } + if fs::metadata(&candidate).is_ok() + && let Ok(manifest) = ManifestPath::try_from(candidate) + { + return Some(manifest); } + curr = path.parent(); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs index 9781c46737d8..c0a5009afba3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs @@ -143,12 +143,11 @@ impl Sysroot { Some(root) => { // special case rustc, we can look that up directly in the sysroot's bin folder // as it should never invoke another cargo binary - if let Tool::Rustc = tool { - if let Some(path) = + if let Tool::Rustc = tool + && let Some(path) = probe_for_binary(root.join("bin").join(Tool::Rustc.name()).into()) - { - return toolchain::command(path, current_dir, envs); - } + { + return toolchain::command(path, current_dir, envs); } let mut cmd = toolchain::command(tool.prefer_proxy(), current_dir, envs); @@ -291,29 +290,26 @@ impl Sysroot { pub fn set_workspace(&mut self, workspace: RustLibSrcWorkspace) { self.workspace = workspace; - if self.error.is_none() { - if let Some(src_root) = &self.rust_lib_src_root { - let has_core = match &self.workspace { - RustLibSrcWorkspace::Workspace(ws) => { - ws.packages().any(|p| ws[p].name == "core") - } - RustLibSrcWorkspace::Json(project_json) => project_json - .crates() - .filter_map(|(_, krate)| krate.display_name.clone()) - .any(|name| name.canonical_name().as_str() == "core"), - RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), - RustLibSrcWorkspace::Empty => true, + if self.error.is_none() + && let Some(src_root) = &self.rust_lib_src_root + { + let has_core = match &self.workspace { + RustLibSrcWorkspace::Workspace(ws) => ws.packages().any(|p| ws[p].name == "core"), + RustLibSrcWorkspace::Json(project_json) => project_json + .crates() + .filter_map(|(_, krate)| krate.display_name.clone()) + .any(|name| name.canonical_name().as_str() == "core"), + RustLibSrcWorkspace::Stitched(stitched) => stitched.by_name("core").is_some(), + RustLibSrcWorkspace::Empty => true, + }; + if !has_core { + let var_note = if env::var_os("RUST_SRC_PATH").is_some() { + " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" + } else { + ", try running `rustup component add rust-src` to possibly fix this" }; - if !has_core { - let var_note = if env::var_os("RUST_SRC_PATH").is_some() { - " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)" - } else { - ", try running `rustup component add rust-src` to possibly fix this" - }; - self.error = Some(format!( - "sysroot at `{src_root}` is missing a `core` library{var_note}", - )); - } + self.error = + Some(format!("sysroot at `{src_root}` is missing a `core` library{var_note}",)); } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 655da118bbd4..f031fef165e6 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -24,7 +24,7 @@ use crate::{ CargoConfig, CargoWorkspace, CfgOverrides, InvocationStrategy, ManifestPath, Package, ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind, WorkspaceBuildScripts, - build_dependencies::BuildScriptOutput, + build_dependencies::{BuildScriptOutput, ProcMacroDylibPath}, cargo_config_file, cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource}, env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env}, @@ -424,12 +424,12 @@ impl ProjectWorkspace { sysroot.set_workspace(loaded_sysroot); } - if !cargo.requires_rustc_private() { - if let Err(e) = &mut rustc { - // We don't need the rustc sources here, - // so just discard the error. - _ = e.take(); - } + if !cargo.requires_rustc_private() + && let Err(e) = &mut rustc + { + // We don't need the rustc sources here, + // so just discard the error. + _ = e.take(); } Ok(ProjectWorkspace { @@ -1163,17 +1163,15 @@ fn project_json_to_crate_graph( crate = display_name.as_ref().map(|name| name.canonical_name().as_str()), "added root to crate graph" ); - if *is_proc_macro { - if let Some(path) = proc_macro_dylib_path.clone() { - let node = Ok(( - display_name - .as_ref() - .map(|it| it.canonical_name().as_str().to_owned()) - .unwrap_or_else(|| format!("crate{}", idx.0)), - path, - )); - proc_macros.insert(crate_graph_crate_id, node); - } + if *is_proc_macro && let Some(path) = proc_macro_dylib_path.clone() { + let node = Ok(( + display_name + .as_ref() + .map(|it| it.canonical_name().as_str().to_owned()) + .unwrap_or_else(|| format!("crate{}", idx.0)), + path, + )); + proc_macros.insert(crate_graph_crate_id, node); } (idx, crate_graph_crate_id) }, @@ -1319,14 +1317,12 @@ fn cargo_to_crate_graph( // Add dep edge of all targets to the package's lib target if let Some((to, name)) = lib_tgt.clone() { - if to != from && kind != TargetKind::BuildScript { - // (build script can not depend on its library target) - - // For root projects with dashes in their name, - // cargo metadata does not do any normalization, - // so we do it ourselves currently - let name = CrateName::normalize_dashes(&name); - add_dep(crate_graph, from, name, to); + match to != from && kind != TargetKind::BuildScript { + true => { + let name = CrateName::normalize_dashes(&name); + add_dep(crate_graph, from, name, to); + } + false => (), } } } @@ -1638,9 +1634,21 @@ fn add_target_crate_root( let proc_macro = match build_data { Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => { match proc_macro_dylib_path { - Some(path) => Ok((cargo_name.to_owned(), path.clone())), - None if has_errors => Err(ProcMacroLoadingError::FailedToBuild), - None => Err(ProcMacroLoadingError::MissingDylibPath), + ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())), + ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt), + ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound(_) + if has_errors => + { + Err(ProcMacroLoadingError::FailedToBuild) + } + ProcMacroDylibPath::NotProcMacro => { + Err(ProcMacroLoadingError::ExpectedProcMacroArtifact) + } + ProcMacroDylibPath::DylibNotFound(candidates) => { + Err(ProcMacroLoadingError::MissingDylibPath( + candidates.iter().map(ToString::to_string).collect(), + )) + } } } None => Err(ProcMacroLoadingError::NotYetBuilt), From 38c61c9ae84a878ea47fb6323403aeacfd27af31 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 20:07:28 +0200 Subject: [PATCH 099/201] Copy lockfile when building build scripts --- .../project-model/src/build_dependencies.rs | 44 ++++++---- .../project-model/src/cargo_config_file.rs | 22 +++++ .../project-model/src/cargo_workspace.rs | 83 ++++++------------- 3 files changed, 76 insertions(+), 73 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 664665625f99..dec1ffc72d6b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -16,12 +16,13 @@ use la_arena::ArenaMap; use paths::{AbsPath, AbsPathBuf, Utf8PathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; use serde::Deserialize as _; -use stdx::{always, never}; +use stdx::never; use toolchain::Tool; use crate::{ CargoConfig, CargoFeatures, CargoWorkspace, InvocationStrategy, ManifestPath, Package, Sysroot, - TargetKind, utf8_stdout, + TargetKind, cargo_config_file::make_lockfile_copy, + cargo_workspace::MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH, utf8_stdout, }; /// Output of the build script and proc-macro building steps for a workspace. @@ -77,7 +78,7 @@ impl WorkspaceBuildScripts { let current_dir = workspace.workspace_root(); let allowed_features = workspace.workspace_features(); - let cmd = Self::build_command( + let (_guard, cmd) = Self::build_command( config, &allowed_features, workspace.manifest_path(), @@ -98,7 +99,7 @@ impl WorkspaceBuildScripts { ) -> io::Result> { assert_eq!(config.invocation_strategy, InvocationStrategy::Once); - let cmd = Self::build_command( + let (_guard, cmd) = Self::build_command( config, &Default::default(), // This is not gonna be used anyways, so just construct a dummy here @@ -379,10 +380,6 @@ impl WorkspaceBuildScripts { progress(format!( "building compile-time-deps: proc-macro {name} built" )); - always!( - data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt, - "received multiple compiler artifacts for the same package: {message:?}" - ); if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } @@ -434,14 +431,15 @@ impl WorkspaceBuildScripts { current_dir: &AbsPath, sysroot: &Sysroot, toolchain: Option<&semver::Version>, - ) -> io::Result { + ) -> io::Result<(Option, Command)> { match config.run_build_script_command.as_deref() { Some([program, args @ ..]) => { let mut cmd = toolchain::command(program, current_dir, &config.extra_env); cmd.args(args); - Ok(cmd) + Ok((None, cmd)) } _ => { + let mut requires_unstable_options = false; let mut cmd = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env); cmd.args(["check", "--quiet", "--workspace", "--message-format=json"]); @@ -457,7 +455,19 @@ impl WorkspaceBuildScripts { if let Some(target) = &config.target { cmd.args(["--target", target]); } - + let mut temp_dir_guard = None; + if toolchain + .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH) + { + let lockfile_path = + <_ as AsRef>::as_ref(manifest_path).with_extension("lock"); + if let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile_path) { + temp_dir_guard = Some(temp_dir); + cmd.arg("--lockfile-path"); + cmd.arg(target_lockfile.as_str()); + requires_unstable_options = true; + } + } match &config.features { CargoFeatures::All => { cmd.arg("--all-features"); @@ -479,6 +489,7 @@ impl WorkspaceBuildScripts { } if manifest_path.is_rust_manifest() { + requires_unstable_options = true; cmd.arg("-Zscript"); } @@ -488,7 +499,7 @@ impl WorkspaceBuildScripts { // available in current toolchain's cargo, use it to build compile time deps only. const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { major: 1, - minor: 89, + minor: 189, patch: 0, pre: semver::Prerelease::EMPTY, build: semver::BuildMetadata::EMPTY, @@ -498,8 +509,7 @@ impl WorkspaceBuildScripts { toolchain.is_some_and(|v| *v >= COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION); if cargo_comp_time_deps_available { - cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); - cmd.arg("-Zunstable-options"); + requires_unstable_options = true; cmd.arg("--compile-time-deps"); // we can pass this unconditionally, because we won't actually build the // binaries, and as such, this will succeed even on targets without libtest @@ -522,7 +532,11 @@ impl WorkspaceBuildScripts { cmd.env("RA_RUSTC_WRAPPER", "1"); } } - Ok(cmd) + if requires_unstable_options { + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); + cmd.arg("-Zunstable-options"); + } + Ok((temp_dir_guard, cmd)) } } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs index 7966f74df305..a1e7ed092324 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs @@ -1,4 +1,5 @@ //! Read `.cargo/config.toml` as a JSON object +use paths::{Utf8Path, Utf8PathBuf}; use rustc_hash::FxHashMap; use toolchain::Tool; @@ -32,3 +33,24 @@ pub(crate) fn read( Some(json) } + +pub(crate) fn make_lockfile_copy( + lockfile_path: &Utf8Path, +) -> Option<(temp_dir::TempDir, Utf8PathBuf)> { + let temp_dir = temp_dir::TempDir::with_prefix("rust-analyzer").ok()?; + let target_lockfile = temp_dir.path().join("Cargo.lock").try_into().ok()?; + match std::fs::copy(lockfile_path, &target_lockfile) { + Ok(_) => { + tracing::debug!("Copied lock file from `{}` to `{}`", lockfile_path, target_lockfile); + Some((temp_dir, target_lockfile)) + } + // lockfile does not yet exist, so we can just create a new one in the temp dir + Err(e) if e.kind() == std::io::ErrorKind::NotFound => Some((temp_dir, target_lockfile)), + Err(e) => { + tracing::warn!( + "Failed to copy lock file from `{lockfile_path}` to `{target_lockfile}`: {e}", + ); + None + } + } +} diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 767a4aade09a..b5f68b2435d6 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -15,16 +15,18 @@ use span::Edition; use stdx::process::spawn_with_streaming_output; use toolchain::Tool; +use crate::cargo_config_file::make_lockfile_copy; use crate::{CfgOverrides, InvocationStrategy}; use crate::{ManifestPath, Sysroot}; -const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = semver::Version { - major: 1, - minor: 82, - patch: 0, - pre: semver::Prerelease::EMPTY, - build: semver::BuildMetadata::EMPTY, -}; +pub(crate) const MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH: semver::Version = + semver::Version { + major: 1, + minor: 82, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. @@ -552,8 +554,10 @@ impl CargoWorkspace { pub(crate) struct FetchMetadata { command: cargo_metadata::MetadataCommand, + #[expect(dead_code)] manifest_path: ManifestPath, lockfile_path: Option, + #[expect(dead_code)] kind: &'static str, no_deps: bool, no_deps_result: anyhow::Result, @@ -634,7 +638,7 @@ impl FetchMetadata { command.other_options(other_options.clone()); if needs_nightly { - command.env("RUSTC_BOOTSTRAP", "1"); + command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); } // Pre-fetch basic metadata using `--no-deps`, which: @@ -681,11 +685,12 @@ impl FetchMetadata { locked: bool, progress: &dyn Fn(String), ) -> anyhow::Result<(cargo_metadata::Metadata, Option)> { + _ = target_dir; let Self { mut command, - manifest_path, + manifest_path: _, lockfile_path, - kind, + kind: _, no_deps, no_deps_result, mut other_options, @@ -696,54 +701,18 @@ impl FetchMetadata { } let mut using_lockfile_copy = false; - let mut _temp_dir_guard = None; - // The manifest is a rust file, so this means its a script manifest - if let Some(lockfile) = lockfile_path { - _temp_dir_guard = temp_dir::TempDir::with_prefix("rust-analyzer").ok(); - let target_lockfile = _temp_dir_guard - .and_then(|tmp| tmp.path().join("Cargo.lock").try_into().ok()) - .unwrap_or_else(|| { - // When multiple workspaces share the same target dir, they might overwrite into a - // single lockfile path. - // See https://github.com/rust-lang/rust-analyzer/issues/20189#issuecomment-3073520255 - let manifest_path_hash = std::hash::BuildHasher::hash_one( - &std::hash::BuildHasherDefault::::default(), - &manifest_path, - ); - let disambiguator = format!( - "{}_{manifest_path_hash}", - manifest_path.components().nth_back(1).map_or("", |c| c.as_str()) - ); - - target_dir - .join("rust-analyzer") - .join("metadata") - .join(kind) - .join(disambiguator) - .join("Cargo.lock") - }); - match std::fs::copy(&lockfile, &target_lockfile) { - Ok(_) => { - using_lockfile_copy = true; - other_options.push("--lockfile-path".to_owned()); - other_options.push(target_lockfile.to_string()); - } - Err(e) if e.kind() == std::io::ErrorKind::NotFound => { - // There exists no lockfile yet - using_lockfile_copy = true; - other_options.push("--lockfile-path".to_owned()); - other_options.push(target_lockfile.to_string()); - } - Err(e) => { - tracing::warn!( - "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}", - ); - } - } + let mut _temp_dir_guard; + if let Some(lockfile) = lockfile_path + && let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile) + { + _temp_dir_guard = temp_dir; + other_options.push("--lockfile-path".to_owned()); + other_options.push(target_lockfile.to_string()); + using_lockfile_copy = true; } if using_lockfile_copy { + command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); other_options.push("-Zunstable-options".to_owned()); - command.env("RUSTC_BOOTSTRAP", "1"); } // No need to lock it if we copied the lockfile, we won't modify the original after all/ // This way cargo cannot error out on us if the lockfile requires updating. @@ -752,13 +721,11 @@ impl FetchMetadata { } command.other_options(other_options); - // FIXME: Fetching metadata is a slow process, as it might require - // calling crates.io. We should be reporting progress here, but it's - // unclear whether cargo itself supports it. progress("cargo metadata: started".to_owned()); let res = (|| -> anyhow::Result<(_, _)> { let mut errored = false; + tracing::debug!("Running `{:?}`", command.cargo_command()); let output = spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| { errored = errored || line.starts_with("error") || line.starts_with("warning"); From 72927f6eb74550e24f0fdeecd5f21201fd970d12 Mon Sep 17 00:00:00 2001 From: FractalFir Date: Sun, 27 Jul 2025 20:55:45 +0200 Subject: [PATCH 100/201] Ensure correct aligement of rustc_hir::Lifetime on platforms with lower default alignments. --- compiler/rustc_hir/src/hir.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e83f6a1df720..36bada15db1e 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -148,6 +148,11 @@ impl From for LifetimeSyntax { /// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing` /// — there's no way to "elide" these lifetimes. #[derive(Debug, Copy, Clone, HashStable_Generic)] +// Raise the aligement to at least 4 bytes - this is relied on in other parts of the compiler(for pointer tagging): +// https://github.com/rust-lang/rust/blob/ce5fdd7d42aba9a2925692e11af2bd39cf37798a/compiler/rustc_data_structures/src/tagged_ptr.rs#L163 +// Removing this `repr(4)` will cause the compiler to not build on platforms like `m68k` Linux, where the aligement of u32 and usize is only 2. +// Since `repr(align)` may only raise aligement, this has no effect on platforms where the aligement is already sufficient. +#[repr(align(4))] pub struct Lifetime { #[stable_hasher(ignore)] pub hir_id: HirId, From c5480a9096e6c0c859902f2fc7529e4119ad7124 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 21:22:50 +0200 Subject: [PATCH 101/201] fix: Fix incorrect build script version check --- .../crates/project-model/src/build_dependencies.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index dec1ffc72d6b..289b033a6ad3 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -499,7 +499,7 @@ impl WorkspaceBuildScripts { // available in current toolchain's cargo, use it to build compile time deps only. const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { major: 1, - minor: 189, + minor: 89, patch: 0, pre: semver::Prerelease::EMPTY, build: semver::BuildMetadata::EMPTY, From 47bfa846f37cf053471656b572a35d2bfca16715 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 13:21:06 -0700 Subject: [PATCH 102/201] Allow more MIR SROA --- compiler/rustc_mir_transform/src/sroa.rs | 36 +++++-------------- ...y.run2-{closure#0}.Inline.panic-abort.diff | 36 +++++++++---------- ....run2-{closure#0}.Inline.panic-unwind.diff | 36 +++++++++---------- 3 files changed, 40 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_mir_transform/src/sroa.rs b/compiler/rustc_mir_transform/src/sroa.rs index 7c6ccc89c4f3..80c4b58a5744 100644 --- a/compiler/rustc_mir_transform/src/sroa.rs +++ b/compiler/rustc_mir_transform/src/sroa.rs @@ -1,4 +1,4 @@ -use rustc_abi::{FIRST_VARIANT, FieldIdx}; +use rustc_abi::FieldIdx; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_hir::LangItem; use rustc_index::IndexVec; @@ -32,7 +32,7 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { let typing_env = body.typing_env(tcx); loop { debug!(?excluded); - let escaping = escaping_locals(tcx, typing_env, &excluded, body); + let escaping = escaping_locals(tcx, &excluded, body); debug!(?escaping); let replacements = compute_flattening(tcx, typing_env, body, escaping); debug!(?replacements); @@ -64,7 +64,6 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates { /// client code. fn escaping_locals<'tcx>( tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, excluded: &DenseBitSet, body: &Body<'tcx>, ) -> DenseBitSet { @@ -72,31 +71,12 @@ fn escaping_locals<'tcx>( if ty.is_union() || ty.is_enum() { return true; } - if let ty::Adt(def, _args) = ty.kind() { - if def.repr().simd() { - // Exclude #[repr(simd)] types so that they are not de-optimized into an array - return true; - } - if tcx.is_lang_item(def.did(), LangItem::DynMetadata) { - // codegen wants to see the `DynMetadata`, - // not the inner reference-to-opaque-type. - return true; - } - // We already excluded unions and enums, so this ADT must have one variant - let variant = def.variant(FIRST_VARIANT); - if variant.fields.len() > 1 { - // If this has more than one field, it cannot be a wrapper that only provides a - // niche, so we do not want to automatically exclude it. - return false; - } - let Ok(layout) = tcx.layout_of(typing_env.as_query_input(ty)) else { - // We can't get the layout - return true; - }; - if layout.layout.largest_niche().is_some() { - // This type has a niche - return true; - } + if let ty::Adt(def, _args) = ty.kind() + && tcx.is_lang_item(def.did(), LangItem::DynMetadata) + { + // codegen wants to see the `DynMetadata`, + // not the inner reference-to-opaque-type. + return true; } // Default for non-ADTs false diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff index 169a6768448f..22e6ea722dda 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -64,9 +64,8 @@ + let mut _45: &mut std::future::Ready<()>; + let mut _46: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _47: std::pin::Pin<&mut std::future::Ready<()>>; + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _48: &mut &mut std::future::Ready<()>; ++ let mut _47: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -76,15 +75,15 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _49: std::option::Option<()>; ++ let mut _48: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _50: isize; -+ let mut _51: !; ++ let mut _49: isize; ++ let mut _50: !; + scope 23 { + } + } @@ -229,28 +228,25 @@ + StorageDead(_24); + StorageLive(_45); + StorageLive(_46); -+ StorageLive(_51); ++ StorageLive(_50); + StorageLive(_42); + StorageLive(_43); + StorageLive(_44); + _46 = &mut _19; + StorageLive(_47); -+ StorageLive(_48); -+ _48 = &mut (_19.0: &mut std::future::Ready<()>); ++ _47 = &mut (_19.0: &mut std::future::Ready<()>); + _45 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_48); -+ _47 = Pin::<&mut std::future::Ready<()>> { pointer: copy _45 }; + StorageDead(_47); + _44 = &mut ((*_45).0: std::option::Option<()>); -+ StorageLive(_49); -+ _49 = Option::<()>::None; ++ StorageLive(_48); ++ _48 = Option::<()>::None; + _43 = copy ((*_45).0: std::option::Option<()>); -+ ((*_45).0: std::option::Option<()>) = copy _49; -+ StorageDead(_49); ++ ((*_45).0: std::option::Option<()>) = copy _48; ++ StorageDead(_48); + StorageDead(_44); -+ StorageLive(_50); -+ _50 = discriminant(_43); -+ switchInt(move _50) -> [0: bb11, 1: bb12, otherwise: bb5]; ++ StorageLive(_49); ++ _49 = discriminant(_43); ++ switchInt(move _49) -> [0: bb11, 1: bb12, otherwise: bb5]; } + + bb5: { @@ -313,16 +309,16 @@ + } + + bb11: { -+ _51 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; ++ _50 = option::expect_failed(const "`Ready` polled after completion") -> unwind unreachable; + } + + bb12: { + _42 = move ((_43 as Some).0: ()); -+ StorageDead(_50); ++ StorageDead(_49); + StorageDead(_43); + _18 = Poll::<()>::Ready(move _42); + StorageDead(_42); -+ StorageDead(_51); ++ StorageDead(_50); + StorageDead(_46); + StorageDead(_45); + StorageDead(_22); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff index 14ba3311d2dc..8b027e988b8e 100644 --- a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -66,9 +66,8 @@ + let mut _47: &mut std::future::Ready<()>; + let mut _48: &mut std::pin::Pin<&mut std::future::Ready<()>>; + scope 14 (inlined > as DerefMut>::deref_mut) { -+ let mut _49: std::pin::Pin<&mut std::future::Ready<()>>; + scope 15 (inlined Pin::<&mut std::future::Ready<()>>::as_mut) { -+ let mut _50: &mut &mut std::future::Ready<()>; ++ let mut _49: &mut &mut std::future::Ready<()>; + scope 16 (inlined Pin::<&mut std::future::Ready<()>>::new_unchecked) { + } + scope 18 (inlined <&mut std::future::Ready<()> as DerefMut>::deref_mut) { @@ -78,15 +77,15 @@ + } + } + scope 19 (inlined Option::<()>::take) { -+ let mut _51: std::option::Option<()>; ++ let mut _50: std::option::Option<()>; + scope 20 (inlined std::mem::replace::>) { + scope 21 { + } + } + } + scope 22 (inlined #[track_caller] Option::<()>::expect) { -+ let mut _52: isize; -+ let mut _53: !; ++ let mut _51: isize; ++ let mut _52: !; + scope 23 { + } + } @@ -246,28 +245,25 @@ + StorageDead(_24); + StorageLive(_47); + StorageLive(_48); -+ StorageLive(_53); ++ StorageLive(_52); + StorageLive(_44); + StorageLive(_45); + StorageLive(_46); + _48 = &mut _19; + StorageLive(_49); -+ StorageLive(_50); -+ _50 = &mut (_19.0: &mut std::future::Ready<()>); ++ _49 = &mut (_19.0: &mut std::future::Ready<()>); + _47 = copy (_19.0: &mut std::future::Ready<()>); -+ StorageDead(_50); -+ _49 = Pin::<&mut std::future::Ready<()>> { pointer: copy _47 }; + StorageDead(_49); + _46 = &mut ((*_47).0: std::option::Option<()>); -+ StorageLive(_51); -+ _51 = Option::<()>::None; ++ StorageLive(_50); ++ _50 = Option::<()>::None; + _45 = copy ((*_47).0: std::option::Option<()>); -+ ((*_47).0: std::option::Option<()>) = copy _51; -+ StorageDead(_51); ++ ((*_47).0: std::option::Option<()>) = copy _50; ++ StorageDead(_50); + StorageDead(_46); -+ StorageLive(_52); -+ _52 = discriminant(_45); -+ switchInt(move _52) -> [0: bb16, 1: bb17, otherwise: bb7]; ++ StorageLive(_51); ++ _51 = discriminant(_45); ++ switchInt(move _51) -> [0: bb16, 1: bb17, otherwise: bb7]; } - bb6 (cleanup): { @@ -354,16 +350,16 @@ + } + + bb16: { -+ _53 = option::expect_failed(const "`Ready` polled after completion") -> bb11; ++ _52 = option::expect_failed(const "`Ready` polled after completion") -> bb11; + } + + bb17: { + _44 = move ((_45 as Some).0: ()); -+ StorageDead(_52); ++ StorageDead(_51); + StorageDead(_45); + _18 = Poll::<()>::Ready(move _44); + StorageDead(_44); -+ StorageDead(_53); ++ StorageDead(_52); + StorageDead(_48); + StorageDead(_47); + StorageDead(_22); From 296586fb026539916a39b79af9a2c3440ce93976 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 16:24:48 +0200 Subject: [PATCH 103/201] miri: for ABI mismatch errors, say which argument is the problem --- compiler/rustc_const_eval/messages.ftl | 6 +++--- compiler/rustc_const_eval/src/errors.rs | 16 ++++++++++------ compiler/rustc_const_eval/src/interpret/call.rs | 10 +++++++--- compiler/rustc_middle/src/mir/interpret/error.rs | 7 ++++++- src/tools/miri/src/diagnostics.rs | 2 +- src/tools/miri/src/helpers.rs | 1 + .../abi_mismatch_array_vs_struct.rs | 2 +- .../abi_mismatch_array_vs_struct.stderr | 2 +- .../abi_mismatch_int_vs_float.rs | 2 +- .../abi_mismatch_int_vs_float.stderr | 2 +- .../abi_mismatch_raw_pointer.rs | 2 +- .../abi_mismatch_raw_pointer.stderr | 2 +- .../function_pointers/abi_mismatch_repr_C.rs | 2 +- .../function_pointers/abi_mismatch_repr_C.stderr | 2 +- .../abi_mismatch_return_type.rs | 2 +- .../abi_mismatch_return_type.stderr | 2 -- .../function_pointers/abi_mismatch_simple.rs | 2 +- .../function_pointers/abi_mismatch_simple.stderr | 2 +- .../function_pointers/abi_mismatch_vector.rs | 2 +- .../function_pointers/abi_mismatch_vector.stderr | 2 +- .../miri/tests/fail/shims/input_arg_mismatch.rs | 2 +- .../tests/fail/shims/input_arg_mismatch.stderr | 2 +- .../tests/fail/shims/return_type_mismatch.stderr | 2 -- .../fail/tail_calls/signature-mismatch-arg.rs | 2 +- .../tail_calls/signature-mismatch-arg.stderr | 2 +- 25 files changed, 45 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index aa0bc42d4485..2985eafb63af 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -128,15 +128,15 @@ const_eval_frame_note_inner = inside {$where_ -> const_eval_frame_note_last = the failure occurred here +const_eval_incompatible_arg_types = + calling a function whose parameter #{$arg_idx} has type {$callee_ty} passing argument of type {$caller_ty} + const_eval_incompatible_calling_conventions = calling a function with calling convention "{$callee_conv}" using calling convention "{$caller_conv}" const_eval_incompatible_return_types = calling a function with return type {$callee_ty} passing return place of type {$caller_ty} -const_eval_incompatible_types = - calling a function with argument of type {$callee_ty} passing data of type {$caller_ty} - const_eval_interior_mutable_borrow_escaping = interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed .label = this borrow of an interior mutable value refers to such a temporary diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index b6a64035261e..a4148cb145ff 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -500,7 +500,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { InvalidNichedEnumVariantWritten { .. } => { const_eval_invalid_niched_enum_variant_written } - AbiMismatchArgument { .. } => const_eval_incompatible_types, + AbiMismatchArgument { .. } => const_eval_incompatible_arg_types, AbiMismatchReturn { .. } => const_eval_incompatible_return_types, } } @@ -625,12 +625,16 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> { diag.arg("data_size", info.data_size); } InvalidNichedEnumVariantWritten { enum_ty } => { - diag.arg("ty", enum_ty.to_string()); + diag.arg("ty", enum_ty); } - AbiMismatchArgument { caller_ty, callee_ty } - | AbiMismatchReturn { caller_ty, callee_ty } => { - diag.arg("caller_ty", caller_ty.to_string()); - diag.arg("callee_ty", callee_ty.to_string()); + AbiMismatchArgument { arg_idx, caller_ty, callee_ty } => { + diag.arg("arg_idx", arg_idx + 1); // adjust for 1-indexed lists in output + diag.arg("caller_ty", caller_ty); + diag.arg("callee_ty", callee_ty); + } + AbiMismatchReturn { caller_ty, callee_ty } => { + diag.arg("caller_ty", caller_ty); + diag.arg("callee_ty", callee_ty); } } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 1503f3bcd990..bda00ea2fb88 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -270,6 +270,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { Item = (&'x FnArg<'tcx, M::Provenance>, &'y ArgAbi<'tcx, Ty<'tcx>>), >, callee_abi: &ArgAbi<'tcx, Ty<'tcx>>, + callee_arg_idx: usize, callee_arg: &mir::Place<'tcx>, callee_ty: Ty<'tcx>, already_live: bool, @@ -298,6 +299,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // Check compatibility if !self.check_argument_compat(caller_abi, callee_abi)? { throw_ub!(AbiMismatchArgument { + arg_idx: callee_arg_idx, caller_ty: caller_abi.layout.ty, callee_ty: callee_abi.layout.ty }); @@ -424,7 +426,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // this is a single iterator (that handles `spread_arg`), then // `pass_argument` would be the loop body. It takes care to // not advance `caller_iter` for ignored arguments. - let mut callee_args_abis = callee_fn_abi.args.iter(); + let mut callee_args_abis = callee_fn_abi.args.iter().enumerate(); for local in body.args_iter() { // Construct the destination place for this argument. At this point all // locals are still dead, so we cannot construct a `PlaceTy`. @@ -445,10 +447,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &[mir::ProjectionElem::Field(FieldIdx::from_usize(i), field_ty)], *self.tcx, ); - let callee_abi = callee_args_abis.next().unwrap(); + let (idx, callee_abi) = callee_args_abis.next().unwrap(); self.pass_argument( &mut caller_args, callee_abi, + idx, &dest, field_ty, /* already_live */ true, @@ -456,10 +459,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } else { // Normal argument. Cannot mark it as live yet, it might be unsized! - let callee_abi = callee_args_abis.next().unwrap(); + let (idx, callee_abi) = callee_args_abis.next().unwrap(); self.pass_argument( &mut caller_args, callee_abi, + idx, &dest, ty, /* already_live */ false, diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs index 2b0cfb865645..3e895c6b2809 100644 --- a/compiler/rustc_middle/src/mir/interpret/error.rs +++ b/compiler/rustc_middle/src/mir/interpret/error.rs @@ -426,7 +426,12 @@ pub enum UndefinedBehaviorInfo<'tcx> { /// Trying to set discriminant to the niched variant, but the value does not match. InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> }, /// ABI-incompatible argument types. - AbiMismatchArgument { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, + AbiMismatchArgument { + /// The index of the argument whose type is wrong. + arg_idx: usize, + caller_ty: Ty<'tcx>, + callee_ty: Ty<'tcx>, + }, /// ABI-incompatible return types. AbiMismatchReturn { caller_ty: Ty<'tcx>, callee_ty: Ty<'tcx> }, } diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 9ecbd31c5b9f..8fdf8d643eae 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -382,7 +382,7 @@ pub fn report_error<'tcx>( helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id)); } } - AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { + AbiMismatchArgument { .. } => { helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")); helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri")); } diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ccfff7fa94bd..15699828e00f 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1079,6 +1079,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .position(|b| !b) { throw_ub!(AbiMismatchArgument { + arg_idx: index, caller_ty: caller_fn_abi.args[index].layout.ty, callee_ty: callee_fn_abi.args[index].layout.ty }); diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs index 4468eb299f3d..26f2e73dd752 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs @@ -17,5 +17,5 @@ fn main() { // These two types have the same size but are still not compatible. let g = unsafe { std::mem::transmute::(f) }; - g(Default::default()) //~ ERROR: calling a function with argument of type S passing data of type [i32; 4] + g(Default::default()) //~ ERROR: type S passing argument of type [i32; 4] } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr index cabefa8bee9b..f793abb0b62f 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_array_vs_struct.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type S passing data of type [i32; 4] +error: Undefined Behavior: calling a function whose parameter #1 has type S passing argument of type [i32; 4] --> tests/fail/function_pointers/abi_mismatch_array_vs_struct.rs:LL:CC | LL | g(Default::default()) diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.rs index a1fda329e8d0..0cca4a132333 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(42) //~ ERROR: calling a function with argument of type f32 passing data of type i32 + g(42) //~ ERROR: type f32 passing argument of type i32 } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr index 52cc48d58cee..3651fc9b3f7a 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_int_vs_float.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type f32 passing data of type i32 +error: Undefined Behavior: calling a function whose parameter #1 has type f32 passing argument of type i32 --> tests/fail/function_pointers/abi_mismatch_int_vs_float.rs:LL:CC | LL | g(42) diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.rs index f0ea5ccfe0f5..053a4a5f2845 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(&42 as *const i32) //~ ERROR: calling a function with argument of type *const [i32] passing data of type *const i32 + g(&42 as *const i32) //~ ERROR: type *const [i32] passing argument of type *const i32 } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr index 2fbb0408c591..88345a0688ca 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_raw_pointer.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type *const [i32] passing data of type *const i32 +error: Undefined Behavior: calling a function whose parameter #1 has type *const [i32] passing argument of type *const i32 --> tests/fail/function_pointers/abi_mismatch_raw_pointer.rs:LL:CC | LL | g(&42 as *const i32) diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.rs index c5900489b4c6..f3dffcc4e867 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.rs @@ -12,5 +12,5 @@ fn main() { let fnptr: fn(S2) = callee; let fnptr: fn(S1) = unsafe { std::mem::transmute(fnptr) }; fnptr(S1(NonZero::new(1).unwrap())); - //~^ ERROR: calling a function with argument of type S2 passing data of type S1 + //~^ ERROR: type S2 passing argument of type S1 } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.stderr index 2c1ac0ee7020..47658395132e 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_repr_C.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type S2 passing data of type S1 +error: Undefined Behavior: calling a function whose parameter #1 has type S2 passing argument of type S1 --> tests/fail/function_pointers/abi_mismatch_repr_C.rs:LL:CC | LL | fnptr(S1(NonZero::new(1).unwrap())); diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs index 0fdab49b94b6..05b645cf75af 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.rs @@ -5,5 +5,5 @@ fn main() { let g = unsafe { std::mem::transmute:: u32, fn()>(f) }; - g() //~ ERROR: calling a function with return type u32 passing return place of type () + g() //~ ERROR: type u32 passing return place of type () } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr index 28c676ad4823..07e6561b53e7 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr @@ -6,8 +6,6 @@ LL | g() | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.rs index 20384f0965b8..ca43c06008fd 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.rs @@ -3,5 +3,5 @@ fn main() { let g = unsafe { std::mem::transmute::(f) }; - g(42) //~ ERROR: calling a function with argument of type (i32, i32) passing data of type i32 + g(42) //~ ERROR: type (i32, i32) passing argument of type i32 } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr index e45ad12ec056..2ed9ac2e6dae 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_simple.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type (i32, i32) passing data of type i32 +error: Undefined Behavior: calling a function whose parameter #1 has type (i32, i32) passing argument of type i32 --> tests/fail/function_pointers/abi_mismatch_simple.rs:LL:CC | LL | g(42) diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.rs b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.rs index 80f357b61bad..dedcaac61427 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.rs +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.rs @@ -7,5 +7,5 @@ fn main() { // These two vector types have the same size but are still not compatible. let g = unsafe { std::mem::transmute::(f) }; - g(Default::default()) //~ ERROR: calling a function with argument of type std::simd::Simd passing data of type std::simd::Simd + g(Default::default()) //~ ERROR: type std::simd::Simd passing argument of type std::simd::Simd } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr index bad2495cb396..b13e8d936dbe 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_vector.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type std::simd::Simd passing data of type std::simd::Simd +error: Undefined Behavior: calling a function whose parameter #1 has type std::simd::Simd passing argument of type std::simd::Simd --> tests/fail/function_pointers/abi_mismatch_vector.rs:LL:CC | LL | g(Default::default()) diff --git a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs index eb8de04dcc41..77699776aea6 100644 --- a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs @@ -16,6 +16,6 @@ fn main() { } as u32; let _ = unsafe { close(fd); - //~^ ERROR: calling a function with argument of type i32 passing data of type u32 + //~^ ERROR: type i32 passing argument of type u32 }; } diff --git a/src/tools/miri/tests/fail/shims/input_arg_mismatch.stderr b/src/tools/miri/tests/fail/shims/input_arg_mismatch.stderr index ce00b624a428..ec27fd5ebb8f 100644 --- a/src/tools/miri/tests/fail/shims/input_arg_mismatch.stderr +++ b/src/tools/miri/tests/fail/shims/input_arg_mismatch.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type i32 passing data of type u32 +error: Undefined Behavior: calling a function whose parameter #1 has type i32 passing argument of type u32 --> tests/fail/shims/input_arg_mismatch.rs:LL:CC | LL | close(fd); diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr index 18ff3067fa03..080ee16a2eb5 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr @@ -6,8 +6,6 @@ LL | close(fd); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information - = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets - = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/shims/return_type_mismatch.rs:LL:CC diff --git a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs index 6df132d32559..36bd1e99cfbc 100644 --- a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs +++ b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.rs @@ -6,7 +6,7 @@ fn main() { // the error should point to `become g(x)`, // but tail calls mess up the backtrace it seems like... f(0); - //~^ error: Undefined Behavior: calling a function with argument of type i32 passing data of type u32 + //~^ error: type i32 passing argument of type u32 } fn f(x: u32) { diff --git a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr index fbb0d3d565dd..cabea5df85dd 100644 --- a/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr +++ b/src/tools/miri/tests/fail/tail_calls/signature-mismatch-arg.stderr @@ -1,4 +1,4 @@ -error: Undefined Behavior: calling a function with argument of type i32 passing data of type u32 +error: Undefined Behavior: calling a function whose parameter #1 has type i32 passing argument of type u32 --> tests/fail/tail_calls/signature-mismatch-arg.rs:LL:CC | LL | f(0); From f94a6167799145ac417fdd7a79a0c2a5d86a9ea9 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 21:22:50 +0200 Subject: [PATCH 104/201] Cleanup unstable flags handling --- .../project-model/src/build_dependencies.rs | 9 ++++++--- .../project-model/src/cargo_workspace.rs | 9 +-------- .../crates/project-model/src/workspace.rs | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index dec1ffc72d6b..9888984934f7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -62,7 +62,10 @@ impl BuildScriptOutput { self.cfgs.is_empty() && self.envs.is_empty() && self.out_dir.is_none() - && self.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt + && matches!( + self.proc_macro_dylib_path, + ProcMacroDylibPath::NotBuilt | ProcMacroDylibPath::NotProcMacro + ) } } @@ -462,10 +465,10 @@ impl WorkspaceBuildScripts { let lockfile_path = <_ as AsRef>::as_ref(manifest_path).with_extension("lock"); if let Some((temp_dir, target_lockfile)) = make_lockfile_copy(&lockfile_path) { + requires_unstable_options = true; temp_dir_guard = Some(temp_dir); cmd.arg("--lockfile-path"); cmd.arg(target_lockfile.as_str()); - requires_unstable_options = true; } } match &config.features { @@ -499,7 +502,7 @@ impl WorkspaceBuildScripts { // available in current toolchain's cargo, use it to build compile time deps only. const COMP_TIME_DEPS_MIN_TOOLCHAIN_VERSION: semver::Version = semver::Version { major: 1, - minor: 189, + minor: 89, patch: 0, pre: semver::Prerelease::EMPTY, build: semver::BuildMetadata::EMPTY, diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index b5f68b2435d6..e613fd590c70 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -601,7 +601,6 @@ impl FetchMetadata { } command.current_dir(current_dir); - let mut needs_nightly = false; let mut other_options = vec![]; // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually // the only relevant flags for metadata here are unstable ones, so we pass those along @@ -611,7 +610,6 @@ impl FetchMetadata { if arg == "-Z" && let Some(arg) = extra_args.next() { - needs_nightly = true; other_options.push("-Z".to_owned()); other_options.push(arg.to_owned()); } @@ -619,7 +617,6 @@ impl FetchMetadata { let mut lockfile_path = None; if cargo_toml.is_rust_manifest() { - needs_nightly = true; other_options.push("-Zscript".to_owned()); } else if config .toolchain_version @@ -637,10 +634,6 @@ impl FetchMetadata { command.other_options(other_options.clone()); - if needs_nightly { - command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); - } - // Pre-fetch basic metadata using `--no-deps`, which: // - avoids fetching registries like crates.io, // - skips dependency resolution and does not modify lockfiles, @@ -710,7 +703,7 @@ impl FetchMetadata { other_options.push(target_lockfile.to_string()); using_lockfile_copy = true; } - if using_lockfile_copy { + if using_lockfile_copy || other_options.iter().any(|it| it.starts_with("-Z")) { command.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); other_options.push("-Zunstable-options".to_owned()); } diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f031fef165e6..f53c0ae7dc2b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1316,14 +1316,17 @@ fn cargo_to_crate_graph( public_deps.add_to_crate_graph(crate_graph, from); // Add dep edge of all targets to the package's lib target - if let Some((to, name)) = lib_tgt.clone() { - match to != from && kind != TargetKind::BuildScript { - true => { - let name = CrateName::normalize_dashes(&name); - add_dep(crate_graph, from, name, to); - } - false => (), - } + if let Some((to, name)) = lib_tgt.clone() + && to != from + && kind != TargetKind::BuildScript + { + // (build script can not depend on its library target) + + // For root projects with dashes in their name, + // cargo metadata does not do any normalization, + // so we do it ourselves currently + let name = CrateName::normalize_dashes(&name); + add_dep(crate_graph, from, name, to); } } } From d846d615aaed6bf923fff012507b8b2f95c52d8c Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 22:31:06 +0200 Subject: [PATCH 105/201] Ignore `Destruct` bounds again --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 9 +++-- .../crates/hir-ty/src/tests/regression.rs | 34 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index f32b6af4d854..d61e7de6672f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -590,9 +590,14 @@ impl<'a> TyLoweringContext<'a> { .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); let pointee_sized = LangItem::PointeeSized .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); - if meta_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + let destruct = LangItem::Destruct + .resolve_trait(ctx.ty_ctx().db, ctx.ty_ctx().resolver.krate()); + let hir_trait_id = trait_ref.hir_trait_id(); + if meta_sized.is_some_and(|it| it == hir_trait_id) + || destruct.is_some_and(|it| it == hir_trait_id) + { // Ignore this bound - } else if pointee_sized.is_some_and(|it| it == trait_ref.hir_trait_id()) { + } else if pointee_sized.is_some_and(|it| it == hir_trait_id) { // Regard this as `?Sized` bound ctx.ty_ctx().unsized_types.insert(self_ty); } else { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 238753e12e4f..c4c17a93c9cd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -2349,3 +2349,37 @@ fn test() { "#]], ); } + +#[test] +fn rust_destruct_option_clone() { + check_types( + r#" +//- minicore: option, drop +fn test(o: &Option) { + o.my_clone(); + //^^^^^^^^^^^^ Option +} +pub trait MyClone: Sized { + fn my_clone(&self) -> Self; +} +impl const MyClone for Option +where + T: ~const MyClone + ~const Destruct, +{ + fn my_clone(&self) -> Self { + match self { + Some(x) => Some(x.my_clone()), + None => None, + } + } +} +impl const MyClone for i32 { + fn my_clone(&self) -> Self { + *self + } +} +#[lang = "destruct"] +pub trait Destruct {} +"#, + ); +} From 81bdfd39106bf2c6cd8caac4709a8465268016ff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 27 Jul 2025 23:21:42 +0200 Subject: [PATCH 106/201] fix: Consider all produced artifacts for proc-macro dylib search --- .../rust-analyzer/crates/base-db/src/input.rs | 13 +++---------- .../crates/project-model/src/build_dependencies.rs | 14 ++++++++------ .../project-model/src/toolchain_info/rustc_cfg.rs | 1 + .../crates/project-model/src/workspace.rs | 8 +++----- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index fdaebeaa934e..0bf4fbdfbd69 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -31,7 +31,7 @@ pub enum ProcMacroLoadingError { Disabled, FailedToBuild, ExpectedProcMacroArtifact, - MissingDylibPath(Box<[String]>), + MissingDylibPath, NotYetBuilt, NoProcMacros, ProcMacroSrvError(Box), @@ -42,7 +42,7 @@ impl ProcMacroLoadingError { ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false, ProcMacroLoadingError::ExpectedProcMacroArtifact | ProcMacroLoadingError::FailedToBuild - | ProcMacroLoadingError::MissingDylibPath(_) + | ProcMacroLoadingError::MissingDylibPath | ProcMacroLoadingError::NoProcMacros | ProcMacroLoadingError::ProcMacroSrvError(_) => true, } @@ -58,19 +58,12 @@ impl fmt::Display for ProcMacroLoadingError { } ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"), ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"), - ProcMacroLoadingError::MissingDylibPath(candidates) if candidates.is_empty() => { + ProcMacroLoadingError::MissingDylibPath => { write!( f, "proc-macro crate built but the dylib path is missing, this indicates a problem with your build system." ) } - ProcMacroLoadingError::MissingDylibPath(candidates) => { - write!( - f, - "proc-macro crate built but the dylib path is missing, this indicates a problem with your build system. Candidates not considered due to not having a dynamic library extension: {}", - candidates.join(", ") - ) - } ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"), ProcMacroLoadingError::NoProcMacros => { write!(f, "proc macro library has no proc macros") diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 9888984934f7..5bea74bed7ed 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -35,7 +35,7 @@ pub struct WorkspaceBuildScripts { #[derive(Debug, Clone, Default, PartialEq, Eq)] pub enum ProcMacroDylibPath { Path(AbsPathBuf), - DylibNotFound(Box<[Utf8PathBuf]>), + DylibNotFound, NotProcMacro, #[default] NotBuilt, @@ -251,7 +251,7 @@ impl WorkspaceBuildScripts { }) { match proc_macro_dylibs.iter().find(|(name, _)| *name == package.name) { Some((_, path)) => ProcMacroDylibPath::Path(path.clone()), - _ => ProcMacroDylibPath::DylibNotFound(Box::default()), + _ => ProcMacroDylibPath::DylibNotFound, } } else { ProcMacroDylibPath::NotProcMacro @@ -386,7 +386,11 @@ impl WorkspaceBuildScripts { if data.proc_macro_dylib_path == ProcMacroDylibPath::NotBuilt { data.proc_macro_dylib_path = ProcMacroDylibPath::NotProcMacro; } - if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro) + if !matches!(data.proc_macro_dylib_path, ProcMacroDylibPath::Path(_)) + && message + .target + .kind + .contains(&cargo_metadata::TargetKind::ProcMacro) { data.proc_macro_dylib_path = match message.filenames.iter().find(|file| is_dylib(file)) { @@ -394,9 +398,7 @@ impl WorkspaceBuildScripts { let filename = AbsPath::assert(filename); ProcMacroDylibPath::Path(filename.to_owned()) } - None => ProcMacroDylibPath::DylibNotFound( - message.filenames.clone().into_boxed_slice(), - ), + None => ProcMacroDylibPath::DylibNotFound, }; } }); diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs index 6e06e88bf7ac..ab69c8e0e4a5 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -65,6 +65,7 @@ fn rustc_print_cfg( let (sysroot, current_dir) = match config { QueryConfig::Cargo(sysroot, cargo_toml, _) => { let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); + cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly"); cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); if let Some(target) = target { cmd.args(["--target", target]); diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f53c0ae7dc2b..5b36e10fd692 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -1639,7 +1639,7 @@ fn add_target_crate_root( match proc_macro_dylib_path { ProcMacroDylibPath::Path(path) => Ok((cargo_name.to_owned(), path.clone())), ProcMacroDylibPath::NotBuilt => Err(ProcMacroLoadingError::NotYetBuilt), - ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound(_) + ProcMacroDylibPath::NotProcMacro | ProcMacroDylibPath::DylibNotFound if has_errors => { Err(ProcMacroLoadingError::FailedToBuild) @@ -1647,10 +1647,8 @@ fn add_target_crate_root( ProcMacroDylibPath::NotProcMacro => { Err(ProcMacroLoadingError::ExpectedProcMacroArtifact) } - ProcMacroDylibPath::DylibNotFound(candidates) => { - Err(ProcMacroLoadingError::MissingDylibPath( - candidates.iter().map(ToString::to_string).collect(), - )) + ProcMacroDylibPath::DylibNotFound => { + Err(ProcMacroLoadingError::MissingDylibPath) } } } From a20692c2d06683a9467a17dbbd58703ff72916c1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 27 Jul 2025 23:22:35 +0200 Subject: [PATCH 107/201] centralize clockid_t interpretation --- src/tools/miri/src/concurrency/thread.rs | 2 +- src/tools/miri/src/shims/time.rs | 104 +++++++++--------- src/tools/miri/src/shims/unix/freebsd/sync.rs | 26 ++--- src/tools/miri/src/shims/unix/sync.rs | 65 ++++------- 4 files changed, 81 insertions(+), 116 deletions(-) diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 878afdf25173..fe1ef86ccd31 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -375,7 +375,7 @@ impl Timeout { } /// The clock to use for the timeout you are asking for. -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum TimeoutClock { Monotonic, RealTime, diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index eb21abc2a455..b5b35797fec2 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -17,73 +17,71 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + fn parse_clockid(&self, clk_id: Scalar) -> Option { + // This clock support is deliberately minimal because a lot of clock types have fiddly + // properties (is it possible for Miri to be suspended independently of the host?). If you + // have a use for another clock type, please open an issue. + let this = self.eval_context_ref(); + + // Portable names that exist everywhere. + if clk_id == this.eval_libc("CLOCK_REALTIME") { + return Some(TimeoutClock::RealTime); + } else if clk_id == this.eval_libc("CLOCK_MONOTONIC") { + return Some(TimeoutClock::Monotonic); + } + + // Some further platform-specific names we support. + match this.tcx.sess.target.os.as_ref() { + "linux" | "freebsd" | "android" => { + // Linux further distinguishes regular and "coarse" clocks, but the "coarse" version + // is just specified to be "faster and less precise", so we treat it like normal + // clocks. + if clk_id == this.eval_libc("CLOCK_REALTIME_COARSE") { + return Some(TimeoutClock::RealTime); + } else if clk_id == this.eval_libc("CLOCK_MONOTONIC_COARSE") { + return Some(TimeoutClock::Monotonic); + } + } + "macos" => { + // `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but + // that's not really something a program running inside Miri can tell, anyway. + // We need to support it because std uses it. + if clk_id == this.eval_libc("CLOCK_UPTIME_RAW") { + return Some(TimeoutClock::Monotonic); + } + } + _ => {} + } + + None + } + fn clock_gettime( &mut self, clk_id_op: &OpTy<'tcx>, tp_op: &OpTy<'tcx>, dest: &MPlaceTy<'tcx>, ) -> InterpResult<'tcx> { - // This clock support is deliberately minimal because a lot of clock types have fiddly - // properties (is it possible for Miri to be suspended independently of the host?). If you - // have a use for another clock type, please open an issue. - let this = self.eval_context_mut(); this.assert_target_os_is_unix("clock_gettime"); - let clockid_t_size = this.libc_ty_layout("clockid_t").size; - let clk_id = this.read_scalar(clk_id_op)?.to_int(clockid_t_size)?; + let clk_id = this.read_scalar(clk_id_op)?; let tp = this.deref_pointer_as(tp_op, this.libc_ty_layout("timespec"))?; - let absolute_clocks; - let mut relative_clocks; - - match this.tcx.sess.target.os.as_ref() { - "linux" | "freebsd" | "android" => { - // Linux, Android, and FreeBSD have two main kinds of clocks. REALTIME clocks return the actual time since the - // Unix epoch, including effects which may cause time to move backwards such as NTP. - // Linux further distinguishes regular and "coarse" clocks, but the "coarse" version - // is just specified to be "faster and less precise", so we implement both the same way. - absolute_clocks = vec![ - this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?, - this.eval_libc("CLOCK_REALTIME_COARSE").to_int(clockid_t_size)?, - ]; - // The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are - // never allowed to go backwards. We don't need to do any additional monotonicity - // enforcement because std::time::Instant already guarantees that it is monotonic. - relative_clocks = vec![ - this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?, - this.eval_libc("CLOCK_MONOTONIC_COARSE").to_int(clockid_t_size)?, - ]; + let duration = match this.parse_clockid(clk_id) { + Some(TimeoutClock::RealTime) => { + this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?; + system_time_to_duration(&SystemTime::now())? } - "macos" => { - absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?]; - relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?]; - // `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but - // that's not really something a program running inside Miri can tell, anyway. - // We need to support it because std uses it. - relative_clocks.push(this.eval_libc("CLOCK_UPTIME_RAW").to_int(clockid_t_size)?); + Some(TimeoutClock::Monotonic) => + this.machine + .monotonic_clock + .now() + .duration_since(this.machine.monotonic_clock.epoch()), + None => { + return this.set_last_error_and_return(LibcError("EINVAL"), dest); } - "solaris" | "illumos" => { - // The REALTIME clock returns the actual time since the Unix epoch. - absolute_clocks = vec![this.eval_libc("CLOCK_REALTIME").to_int(clockid_t_size)?]; - // MONOTONIC, in the other hand, is the high resolution, non-adjustable - // clock from an arbitrary time in the past. - // Note that the man page mentions HIGHRES but it is just - // an alias of MONOTONIC and the libc crate does not expose it anyway. - // https://docs.oracle.com/cd/E23824_01/html/821-1465/clock-gettime-3c.html - relative_clocks = vec![this.eval_libc("CLOCK_MONOTONIC").to_int(clockid_t_size)?]; - } - target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"), - } - - let duration = if absolute_clocks.contains(&clk_id) { - this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?; - system_time_to_duration(&SystemTime::now())? - } else if relative_clocks.contains(&clk_id) { - this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch()) - } else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; let tv_sec = duration.as_secs(); diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index f4e7d9e58f9f..13d30e05573a 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -228,26 +228,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let abs_time_flag = flags == abs_time; let clock_id_place = this.project_field(ut, FieldIdx::from_u32(2))?; - let clock_id = this.read_scalar(&clock_id_place)?.to_i32()?; - let timeout_clock = this.translate_umtx_time_clock_id(clock_id)?; + let clock_id = this.read_scalar(&clock_id_place)?; + let Some(timeout_clock) = this.parse_clockid(clock_id) else { + throw_unsup_format!("unsupported clock") + }; + if timeout_clock == TimeoutClock::RealTime { + this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME`")?; + } interp_ok(Some(UmtxTime { timeout: duration, abs_time: abs_time_flag, timeout_clock })) } - - /// Translate raw FreeBSD clockid to a Miri TimeoutClock. - /// FIXME: share this code with the pthread and clock_gettime shims. - fn translate_umtx_time_clock_id(&mut self, raw_id: i32) -> InterpResult<'tcx, TimeoutClock> { - let this = self.eval_context_mut(); - - let timeout = if raw_id == this.eval_libc_i32("CLOCK_REALTIME") { - // RealTime clock can't be used in isolation mode. - this.check_no_isolation("`_umtx_op` with `CLOCK_REALTIME` timeout")?; - TimeoutClock::RealTime - } else if raw_id == this.eval_libc_i32("CLOCK_MONOTONIC") { - TimeoutClock::Monotonic - } else { - throw_unsup_format!("unsupported clock id {raw_id}"); - }; - interp_ok(timeout) - } } diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index e20e3b79c3be..5ad4fd501a60 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -297,14 +297,13 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u fn condattr_get_clock_id<'tcx>( ecx: &MiriInterpCx<'tcx>, attr_ptr: &OpTy<'tcx>, -) -> InterpResult<'tcx, i32> { +) -> InterpResult<'tcx, Scalar> { ecx.deref_pointer_and_read( attr_ptr, condattr_clock_offset(ecx)?, ecx.libc_ty_layout("pthread_condattr_t"), ecx.machine.layouts.i32, - )? - .to_i32() + ) } fn condattr_set_clock_id<'tcx>( @@ -321,20 +320,6 @@ fn condattr_set_clock_id<'tcx>( ) } -/// Translates the clock from what is stored in pthread_condattr_t to our enum. -fn condattr_translate_clock_id<'tcx>( - ecx: &MiriInterpCx<'tcx>, - raw_id: i32, -) -> InterpResult<'tcx, ClockId> { - interp_ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") { - ClockId::Realtime - } else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") { - ClockId::Monotonic - } else { - throw_unsup_format!("unsupported clock id: {raw_id}"); - }) -} - // # pthread_cond_t // We store some data directly inside the type, ignoring the platform layout: // - init: u32 @@ -363,22 +348,16 @@ fn cond_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size> interp_ok(offset) } -#[derive(Debug, Clone, Copy)] -enum ClockId { - Realtime, - Monotonic, -} - #[derive(Debug, Clone)] struct PthreadCondvar { condvar_ref: CondvarRef, - clock: ClockId, + clock: TimeoutClock, } fn cond_create<'tcx>( ecx: &mut MiriInterpCx<'tcx>, cond_ptr: &OpTy<'tcx>, - clock: ClockId, + clock: TimeoutClock, ) -> InterpResult<'tcx, PthreadCondvar> { let cond = ecx.deref_pointer_as(cond_ptr, ecx.libc_ty_layout("pthread_cond_t"))?; let data = PthreadCondvar { condvar_ref: CondvarRef::new(), clock }; @@ -407,7 +386,10 @@ where throw_unsup_format!("unsupported static initializer used for `pthread_cond_t`"); } // This used the static initializer. The clock there is always CLOCK_REALTIME. - interp_ok(PthreadCondvar { condvar_ref: CondvarRef::new(), clock: ClockId::Realtime }) + interp_ok(PthreadCondvar { + condvar_ref: CondvarRef::new(), + clock: TimeoutClock::RealTime, + }) }, ) } @@ -742,11 +724,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let clock_id = this.read_scalar(clock_id_op)?.to_i32()?; - if clock_id == this.eval_libc_i32("CLOCK_REALTIME") - || clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") - { - condattr_set_clock_id(this, attr_op, clock_id)?; + let clock_id = this.read_scalar(clock_id_op)?; + if this.parse_clockid(clock_id).is_some() { + condattr_set_clock_id(this, attr_op, clock_id.to_i32()?)?; } else { let einval = this.eval_libc_i32("EINVAL"); return interp_ok(Scalar::from_i32(einval)); @@ -764,7 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let clock_id = condattr_get_clock_id(this, attr_op)?; this.write_scalar( - Scalar::from_i32(clock_id), + clock_id, &this.deref_pointer_as(clk_id_op, this.libc_ty_layout("clockid_t"))?, )?; @@ -799,13 +779,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let attr = this.read_pointer(attr_op)?; // Default clock if `attr` is null, and on macOS where there is no clock attribute. let clock_id = if this.ptr_is_null(attr)? || this.tcx.sess.target.os == "macos" { - this.eval_libc_i32("CLOCK_REALTIME") + this.eval_libc("CLOCK_REALTIME") } else { condattr_get_clock_id(this, attr_op)? }; - let clock_id = condattr_translate_clock_id(this, clock_id)?; + let Some(clock) = this.parse_clockid(clock_id) else { + // This is UB since this situation cannot arise when using pthread_condattr_setclock. + throw_ub_format!("pthread_cond_init: invalid attributes (unsupported clock)") + }; - cond_create(this, cond_op, clock_id)?; + cond_create(this, cond_op, clock)?; interp_ok(()) } @@ -870,18 +853,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); } }; - let timeout_clock = match data.clock { - ClockId::Realtime => { - this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; - TimeoutClock::RealTime - } - ClockId::Monotonic => TimeoutClock::Monotonic, - }; + if data.clock == TimeoutClock::RealTime { + this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; + } this.condvar_wait( data.condvar_ref, mutex_ref, - Some((timeout_clock, TimeoutAnchor::Absolute, duration)), + Some((data.clock, TimeoutAnchor::Absolute, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), From 7db72f82cce7be6e905651fea58881fc66f13a9b Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 28 Jul 2025 01:00:48 +0000 Subject: [PATCH 108/201] Complete span lowering. --- compiler/rustc_ast_lowering/src/block.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 37 ++++++++++++++++------- compiler/rustc_ast_lowering/src/format.rs | 2 ++ compiler/rustc_ast_lowering/src/item.rs | 5 +-- compiler/rustc_ast_lowering/src/lib.rs | 15 +++++++-- 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index c3222b79e55c..2cc07694afbc 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -95,7 +95,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { // Let statements are allowed to have impl trait in bindings. - let super_ = l.super_; + let super_ = l.super_.map(|span| self.lower_span(span)); let ty = l.ty.as_ref().map(|t| { self.lower_ty(t, self.impl_trait_in_bindings_ctxt(ImplTraitPosition::Variable)) }); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 15e736261d58..657792c93971 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -282,9 +282,11 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Field(el, ident) => { hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident)) } - ExprKind::Index(el, er, brackets_span) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span) - } + ExprKind::Index(el, er, brackets_span) => hir::ExprKind::Index( + self.lower_expr(el), + self.lower_expr(er), + self.lower_span(*brackets_span), + ), ExprKind::Range(e1, e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), *lims) } @@ -334,7 +336,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Struct(se) => { let rest = match &se.rest { StructRest::Base(e) => hir::StructTailExpr::Base(self.lower_expr(e)), - StructRest::Rest(sp) => hir::StructTailExpr::DefaultFields(*sp), + StructRest::Rest(sp) => { + hir::StructTailExpr::DefaultFields(self.lower_span(*sp)) + } StructRest::None => hir::StructTailExpr::None, }; hir::ExprKind::Struct( @@ -678,6 +682,14 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::Arm { hir_id, pat, guard, body, span } } + fn lower_capture_clause(&mut self, capture_clause: CaptureBy) -> CaptureBy { + match capture_clause { + CaptureBy::Ref => CaptureBy::Ref, + CaptureBy::Use { use_kw } => CaptureBy::Use { use_kw: self.lower_span(use_kw) }, + CaptureBy::Value { move_kw } => CaptureBy::Value { move_kw: self.lower_span(move_kw) }, + } + } + /// Lower/desugar a coroutine construct. /// /// In particular, this creates the correct async resume argument and `_task_context`. @@ -769,7 +781,7 @@ impl<'hir> LoweringContext<'_, 'hir> { hir::ExprKind::Closure(self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: hir::ClosureBinder::Default, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params: &[], fn_decl, body, @@ -1035,7 +1047,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } fn lower_expr_use(&mut self, use_kw_span: Span, expr: &Expr) -> hir::ExprKind<'hir> { - hir::ExprKind::Use(self.lower_expr(expr), use_kw_span) + hir::ExprKind::Use(self.lower_expr(expr), self.lower_span(use_kw_span)) } fn lower_expr_closure( @@ -1083,7 +1095,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: binder_clause, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params, fn_decl, body: body_id, @@ -1197,7 +1209,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let c = self.arena.alloc(hir::Closure { def_id: closure_def_id, binder: binder_clause, - capture_clause, + capture_clause: self.lower_capture_clause(capture_clause), bound_generic_params, fn_decl, body, @@ -2101,7 +2113,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn expr_uint(&mut self, sp: Span, ty: ast::UintTy, value: u128) -> hir::Expr<'hir> { let lit = hir::Lit { - span: sp, + span: self.lower_span(sp), node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ty)), }; self.expr(sp, hir::ExprKind::Lit(lit)) @@ -2120,7 +2132,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> { - let lit = hir::Lit { span: sp, node: ast::LitKind::Str(value, ast::StrStyle::Cooked) }; + let lit = hir::Lit { + span: self.lower_span(sp), + node: ast::LitKind::Str(value, ast::StrStyle::Cooked), + }; self.expr(sp, hir::ExprKind::Lit(lit)) } @@ -2206,7 +2221,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(hir::Path { span: self.lower_span(span), res, - segments: arena_vec![self; hir::PathSegment::new(ident, hir_id, res)], + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], }), )); diff --git a/compiler/rustc_ast_lowering/src/format.rs b/compiler/rustc_ast_lowering/src/format.rs index 5b1dcab87b92..ec9d26eb33f4 100644 --- a/compiler/rustc_ast_lowering/src/format.rs +++ b/compiler/rustc_ast_lowering/src/format.rs @@ -402,6 +402,8 @@ fn expand_format_args<'hir>( fmt: &FormatArgs, allow_const: bool, ) -> hir::ExprKind<'hir> { + let macsp = ctx.lower_span(macsp); + let mut incomplete_lit = String::new(); let lit_pieces = ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ddf01b69e7f6..1899dcda3619 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -627,6 +627,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } else { // For non-empty lists we can just drop all the data, the prefix is already // present in HIR as a part of nested imports. + let span = self.lower_span(span); self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span }) }; hir::ItemKind::Use(path, hir::UseKind::ListStem) @@ -1567,7 +1568,7 @@ impl<'hir> LoweringContext<'_, 'hir> { attrs: &[hir::Attribute], ) -> hir::FnHeader { let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coroutine_kind { - hir::IsAsync::Async(span) + hir::IsAsync::Async(self.lower_span(span)) } else { hir::IsAsync::NotAsync }; @@ -1804,7 +1805,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let res = Res::Def(DefKind::TyParam, def_id); let ident = self.lower_ident(ident); let ty_path = self.arena.alloc(hir::Path { - span: param_span, + span: self.lower_span(param_span), res, segments: self .arena diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9aef189a29d4..189c82b614c2 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2368,7 +2368,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { &mut self, modifiers: TraitBoundModifiers, ) -> hir::TraitBoundModifiers { - hir::TraitBoundModifiers { constness: modifiers.constness, polarity: modifiers.polarity } + let constness = match modifiers.constness { + BoundConstness::Never => BoundConstness::Never, + BoundConstness::Always(span) => BoundConstness::Always(self.lower_span(span)), + BoundConstness::Maybe(span) => BoundConstness::Maybe(self.lower_span(span)), + }; + let polarity = match modifiers.polarity { + BoundPolarity::Positive => BoundPolarity::Positive, + BoundPolarity::Negative(span) => BoundPolarity::Negative(self.lower_span(span)), + BoundPolarity::Maybe(span) => BoundPolarity::Maybe(self.lower_span(span)), + }; + hir::TraitBoundModifiers { constness, polarity } } // Helper methods for building HIR. @@ -2414,6 +2424,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { init: Option<&'hir hir::Expr<'hir>>, ) -> hir::Stmt<'hir> { let hir_id = self.next_id(); + let span = self.lower_span(span); let local = hir::LetStmt { super_: Some(span), hir_id, @@ -2421,7 +2432,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { pat, els: None, source: hir::LocalSource::Normal, - span: self.lower_span(span), + span, ty: None, }; self.stmt(span, hir::StmtKind::Let(self.arena.alloc(local))) From 43725ed819c57b86b32a66c40572246b1f5b8952 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 26 Jul 2025 06:21:22 +0500 Subject: [PATCH 109/201] use let chains in ast, borrowck, codegen, const_eval --- compiler/rustc_ast_lowering/src/index.rs | 8 +- .../rustc_ast_passes/src/ast_validation.rs | 20 +- compiler/rustc_ast_passes/src/feature_gate.rs | 15 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 8 +- .../src/diagnostics/conflict_errors.rs | 335 +++++++++--------- .../src/diagnostics/explain_borrow.rs | 37 +- .../rustc_borrowck/src/diagnostics/mod.rs | 64 ++-- .../src/diagnostics/move_errors.rs | 57 ++- .../src/diagnostics/region_name.rs | 10 +- .../rustc_borrowck/src/region_infer/mod.rs | 68 ++-- compiler/rustc_borrowck/src/type_check/mod.rs | 26 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 8 +- compiler/rustc_codegen_llvm/src/builder.rs | 8 +- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +- .../rustc_codegen_ssa/src/codegen_attrs.rs | 20 +- .../src/check_consts/check.rs | 8 +- .../rustc_const_eval/src/check_consts/ops.rs | 27 +- .../src/check_consts/resolver.rs | 16 +- .../src/interpret/eval_context.rs | 8 +- .../src/interpret/validity.rs | 8 +- compiler/rustc_errors/src/styled_buffer.rs | 11 +- compiler/rustc_hir_analysis/src/collect.rs | 11 +- .../src/hir_ty_lowering/bounds.rs | 20 +- .../rustc_hir_analysis/src/hir_wf_check.rs | 17 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 8 +- .../src/fn_ctxt/suggestions.rs | 46 +-- 26 files changed, 422 insertions(+), 454 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 1ef64f5a3526..5b63206d7d62 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -164,11 +164,11 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { fn visit_item(&mut self, i: &'hir Item<'hir>) { debug_assert_eq!(i.owner_id, self.owner); self.with_parent(i.hir_id(), |this| { - if let ItemKind::Struct(_, _, struct_def) = &i.kind { + if let ItemKind::Struct(_, _, struct_def) = &i.kind // If this is a tuple or unit-like struct, register the constructor. - if let Some(ctor_hir_id) = struct_def.ctor_hir_id() { - this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); - } + && let Some(ctor_hir_id) = struct_def.ctor_hir_id() + { + this.insert(i.span, ctor_hir_id, Node::Ctor(struct_def)); } intravisit::walk_item(this, i); }); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index a08dae111530..895a457ec1d5 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -640,16 +640,16 @@ impl<'a> AstValidator<'a> { return; } - if let Some(header) = fk.header() { - if let Const::Yes(const_span) = header.constness { - let mut spans = variadic_spans.clone(); - spans.push(const_span); - self.dcx().emit_err(errors::ConstAndCVariadic { - spans, - const_span, - variadic_spans: variadic_spans.clone(), - }); - } + if let Some(header) = fk.header() + && let Const::Yes(const_span) = header.constness + { + let mut spans = variadic_spans.clone(); + spans.push(const_span); + self.dcx().emit_err(errors::ConstAndCVariadic { + spans, + const_span, + variadic_spans: variadic_spans.clone(), + }); } match (fk.ctxt(), fk.header()) { diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 8114733f4067..662357ce8841 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,16 +630,11 @@ fn check_incompatible_features(sess: &Session, features: &Features) { .iter() .filter(|(f1, f2)| features.enabled(*f1) && features.enabled(*f2)) { - if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) { - if let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) - { - let spans = vec![f1_span, f2_span]; - sess.dcx().emit_err(errors::IncompatibleFeatures { - spans, - f1: f1_name, - f2: f2_name, - }); - } + if let Some((f1_name, f1_span)) = enabled_features.clone().find(|(name, _)| name == f1) + && let Some((f2_name, f2_span)) = enabled_features.clone().find(|(name, _)| name == f2) + { + let spans = vec![f1_span, f2_span]; + sess.dcx().emit_err(errors::IncompatibleFeatures { spans, f1: f1_name, f2: f2_name }); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index def0cb74d295..f0cf0c1487f7 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -572,10 +572,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere } fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option) { - if let Some(cmnts) = self.comments_mut() { - if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) { - self.print_comment(cmnt); - } + if let Some(cmnts) = self.comments_mut() + && let Some(cmnt) = cmnts.trailing_comment(span, next_pos) + { + self.print_comment(cmnt); } } diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 040a0607db56..17ae671b138b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2481,13 +2481,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // Check that the parent of the closure is a method call, // with receiver matching with local's type (modulo refs) - if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) { - if let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind { - let recv_ty = typeck_results.expr_ty(recv); + if let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_expr.hir_id) + && let hir::ExprKind::MethodCall(_, recv, ..) = parent.kind + { + let recv_ty = typeck_results.expr_ty(recv); - if recv_ty.peel_refs() != local_ty { - return; - } + if recv_ty.peel_refs() != local_ty { + return; } } @@ -2753,16 +2753,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // With the place of a union and a field access into it, we traverse the second // borrowed place and look for an access to a different field of the same union. for (place_base, elem) in second_borrowed_place.iter_projections().rev() { - if let ProjectionElem::Field(field, _) = elem { - if let Some(union_ty) = union_ty(place_base) { - if field != target_field && place_base == target_base { - return Some(( - self.describe_any_place(place_base), - self.describe_any_place(first_borrowed_place.as_ref()), - self.describe_any_place(second_borrowed_place.as_ref()), - union_ty.to_string(), - )); - } + if let ProjectionElem::Field(field, _) = elem + && let Some(union_ty) = union_ty(place_base) + { + if field != target_field && place_base == target_base { + return Some(( + self.describe_any_place(place_base), + self.describe_any_place(first_borrowed_place.as_ref()), + self.describe_any_place(second_borrowed_place.as_ref()), + union_ty.to_string(), + )); } } } @@ -2949,16 +2949,15 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { from_closure: false, .. } = explanation - { - if let Err(diag) = self.try_report_cannot_return_reference_to_local( + && let Err(diag) = self.try_report_cannot_return_reference_to_local( borrow, borrow_span, span, category, opt_place_desc.as_ref(), - ) { - return diag; - } + ) + { + return diag; } let name = format!("`{name}`"); @@ -3720,30 +3719,30 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let loan_span = loan_spans.args_or_use(); let descr_place = self.describe_any_place(place.as_ref()); - if let BorrowKind::Fake(_) = loan.kind { - if let Some(section) = self.classify_immutable_section(loan.assigned_place) { - let mut err = self.cannot_mutate_in_immutable_section( - span, - loan_span, - &descr_place, - section, - "assign", - ); + if let BorrowKind::Fake(_) = loan.kind + && let Some(section) = self.classify_immutable_section(loan.assigned_place) + { + let mut err = self.cannot_mutate_in_immutable_section( + span, + loan_span, + &descr_place, + section, + "assign", + ); - loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { - use crate::session_diagnostics::CaptureVarCause::*; - match kind { - hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, - hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { - BorrowUseInClosure { var_span } - } + loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| { + use crate::session_diagnostics::CaptureVarCause::*; + match kind { + hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span }, + hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => { + BorrowUseInClosure { var_span } } - }); + } + }); - self.buffer_error(err); + self.buffer_error(err); - return; - } + return; } let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place); @@ -3996,119 +3995,116 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { "annotate_argument_and_return_for_borrow: target={:?} stmt={:?}", target, stmt ); - if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if let Some(assigned_to) = place.as_local() { - debug!( - "annotate_argument_and_return_for_borrow: assigned_to={:?} \ + if let StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(assigned_to) = place.as_local() + { + debug!( + "annotate_argument_and_return_for_borrow: assigned_to={:?} \ rvalue={:?}", - assigned_to, rvalue - ); - // Check if our `target` was captured by a closure. - if let Rvalue::Aggregate( - box AggregateKind::Closure(def_id, args), - operands, - ) = rvalue - { - let def_id = def_id.expect_local(); - for operand in operands { - let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = - operand - else { - continue; - }; - debug!( - "annotate_argument_and_return_for_borrow: assigned_from={:?}", - assigned_from - ); + assigned_to, rvalue + ); + // Check if our `target` was captured by a closure. + if let Rvalue::Aggregate(box AggregateKind::Closure(def_id, args), operands) = + rvalue + { + let def_id = def_id.expect_local(); + for operand in operands { + let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = + operand + else { + continue; + }; + debug!( + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from + ); - // Find the local from the operand. - let Some(assigned_from_local) = - assigned_from.local_or_deref_local() - else { - continue; - }; + // Find the local from the operand. + let Some(assigned_from_local) = assigned_from.local_or_deref_local() + else { + continue; + }; - if assigned_from_local != target { - continue; - } - - // If a closure captured our `target` and then assigned - // into a place then we should annotate the closure in - // case it ends up being assigned into the return place. - annotated_closure = - self.annotate_fn_sig(def_id, args.as_closure().sig()); - debug!( - "annotate_argument_and_return_for_borrow: \ - annotated_closure={:?} assigned_from_local={:?} \ - assigned_to={:?}", - annotated_closure, assigned_from_local, assigned_to - ); - - if assigned_to == mir::RETURN_PLACE { - // If it was assigned directly into the return place, then - // return now. - return annotated_closure; - } else { - // Otherwise, update the target. - target = assigned_to; - } + if assigned_from_local != target { + continue; } - // If none of our closure's operands matched, then skip to the next - // statement. - continue; + // If a closure captured our `target` and then assigned + // into a place then we should annotate the closure in + // case it ends up being assigned into the return place. + annotated_closure = + self.annotate_fn_sig(def_id, args.as_closure().sig()); + debug!( + "annotate_argument_and_return_for_borrow: \ + annotated_closure={:?} assigned_from_local={:?} \ + assigned_to={:?}", + annotated_closure, assigned_from_local, assigned_to + ); + + if assigned_to == mir::RETURN_PLACE { + // If it was assigned directly into the return place, then + // return now. + return annotated_closure; + } else { + // Otherwise, update the target. + target = assigned_to; + } } - // Otherwise, look at other types of assignment. - let assigned_from = match rvalue { - Rvalue::Ref(_, _, assigned_from) => assigned_from, - Rvalue::Use(operand) => match operand { - Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { - assigned_from - } - _ => continue, - }, - _ => continue, - }; - debug!( - "annotate_argument_and_return_for_borrow: \ - assigned_from={:?}", - assigned_from, - ); - - // Find the local from the rvalue. - let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { - continue; - }; - debug!( - "annotate_argument_and_return_for_borrow: \ - assigned_from_local={:?}", - assigned_from_local, - ); - - // Check if our local matches the target - if so, we've assigned our - // borrow to a new place. - if assigned_from_local != target { - continue; - } - - // If we assigned our `target` into a new place, then we should - // check if it was the return place. - debug!( - "annotate_argument_and_return_for_borrow: \ - assigned_from_local={:?} assigned_to={:?}", - assigned_from_local, assigned_to - ); - if assigned_to == mir::RETURN_PLACE { - // If it was then return the annotated closure if there was one, - // else, annotate this function. - return annotated_closure.or_else(fallback); - } - - // If we didn't assign into the return place, then we just update - // the target. - target = assigned_to; + // If none of our closure's operands matched, then skip to the next + // statement. + continue; } + + // Otherwise, look at other types of assignment. + let assigned_from = match rvalue { + Rvalue::Ref(_, _, assigned_from) => assigned_from, + Rvalue::Use(operand) => match operand { + Operand::Copy(assigned_from) | Operand::Move(assigned_from) => { + assigned_from + } + _ => continue, + }, + _ => continue, + }; + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from={:?}", + assigned_from, + ); + + // Find the local from the rvalue. + let Some(assigned_from_local) = assigned_from.local_or_deref_local() else { + continue; + }; + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from_local={:?}", + assigned_from_local, + ); + + // Check if our local matches the target - if so, we've assigned our + // borrow to a new place. + if assigned_from_local != target { + continue; + } + + // If we assigned our `target` into a new place, then we should + // check if it was the return place. + debug!( + "annotate_argument_and_return_for_borrow: \ + assigned_from_local={:?} assigned_to={:?}", + assigned_from_local, assigned_to + ); + if assigned_to == mir::RETURN_PLACE { + // If it was then return the annotated closure if there was one, + // else, annotate this function. + return annotated_closure.or_else(fallback); + } + + // If we didn't assign into the return place, then we just update + // the target. + target = assigned_to; } } @@ -4120,32 +4116,31 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { ); if let TerminatorKind::Call { destination, target: Some(_), args, .. } = &terminator.kind + && let Some(assigned_to) = destination.as_local() { - if let Some(assigned_to) = destination.as_local() { + debug!( + "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", + assigned_to, args + ); + for operand in args { + let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = + &operand.node + else { + continue; + }; debug!( - "annotate_argument_and_return_for_borrow: assigned_to={:?} args={:?}", - assigned_to, args + "annotate_argument_and_return_for_borrow: assigned_from={:?}", + assigned_from, ); - for operand in args { - let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = - &operand.node - else { - continue; - }; + + if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { debug!( - "annotate_argument_and_return_for_borrow: assigned_from={:?}", - assigned_from, + "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", + assigned_from_local, ); - if let Some(assigned_from_local) = assigned_from.local_or_deref_local() { - debug!( - "annotate_argument_and_return_for_borrow: assigned_from_local={:?}", - assigned_from_local, - ); - - if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { - return annotated_closure.or_else(fallback); - } + if assigned_to == mir::RETURN_PLACE && assigned_from_local == target { + return annotated_closure.or_else(fallback); } } } @@ -4244,10 +4239,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { // as the HIR doesn't have full types for closure arguments. let return_ty = sig.output().skip_binder(); let mut return_span = fn_decl.output.span(); - if let hir::FnRetTy::Return(ty) = &fn_decl.output { - if let hir::TyKind::Ref(lifetime, _) = ty.kind { - return_span = lifetime.ident.span; - } + if let hir::FnRetTy::Return(ty) = &fn_decl.output + && let hir::TyKind::Ref(lifetime, _) = ty.kind + { + return_span = lifetime.ident.span; } Some(AnnotatedBorrowFnSignature::NamedFunction { diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index f9e52239d6f6..a10da08ddf34 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -917,30 +917,29 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { if let TerminatorKind::Call { destination, target: Some(block), args, .. } = &terminator.kind + && let Some(dest) = destination.as_local() { - if let Some(dest) = destination.as_local() { - debug!( - "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", - target, dest, args - ); - // Check if one of the arguments to this function is the target place. - let found_target = args.iter().any(|arg| { - if let Operand::Move(place) = arg.node { - if let Some(potential) = place.as_local() { - potential == target - } else { - false - } + debug!( + "was_captured_by_trait_object: target={:?} dest={:?} args={:?}", + target, dest, args + ); + // Check if one of the arguments to this function is the target place. + let found_target = args.iter().any(|arg| { + if let Operand::Move(place) = arg.node { + if let Some(potential) = place.as_local() { + potential == target } else { false } - }); - - // If it is, follow this to the next block and update the target. - if found_target { - target = dest; - queue.push(block.start_location()); + } else { + false } + }); + + // If it is, follow this to the next block and update the target. + if found_target { + target = dest; + queue.push(block.start_location()); } } } diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 9ad91d605a77..ed4cd52ed9c8 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -266,48 +266,44 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { args, .. } = &terminator.kind + && let ty::FnDef(id, _) = *const_.ty().kind() { - if let ty::FnDef(id, _) = *const_.ty().kind() { - debug!("add_moved_or_invoked_closure_note: id={:?}", id); - if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) { - let closure = match args.first() { - Some(Spanned { - node: Operand::Copy(place) | Operand::Move(place), .. - }) if target == place.local_or_deref_local() => { - place.local_or_deref_local().unwrap() - } - _ => return false, - }; + debug!("add_moved_or_invoked_closure_note: id={:?}", id); + if self.infcx.tcx.is_lang_item(self.infcx.tcx.parent(id), LangItem::FnOnce) { + let closure = match args.first() { + Some(Spanned { node: Operand::Copy(place) | Operand::Move(place), .. }) + if target == place.local_or_deref_local() => + { + place.local_or_deref_local().unwrap() + } + _ => return false, + }; - debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); - if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { - let did = did.expect_local(); - if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic(OnClosureNote::InvokedTwice { - place_name: &ty::place_to_string_for_capture( - self.infcx.tcx, - hir_place, - ), - span: *span, - }); - return true; - } + debug!("add_moved_or_invoked_closure_note: closure={:?}", closure); + if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() { + let did = did.expect_local(); + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { + diag.subdiagnostic(OnClosureNote::InvokedTwice { + place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), + span: *span, + }); + return true; } } } } // Check if we are just moving a closure after it has been invoked. - if let Some(target) = target { - if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() { - let did = did.expect_local(); - if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { - diag.subdiagnostic(OnClosureNote::MovedTwice { - place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), - span: *span, - }); - return true; - } + if let Some(target) = target + && let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() + { + let did = did.expect_local(); + if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) { + diag.subdiagnostic(OnClosureNote::MovedTwice { + place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place), + span: *span, + }); + return true; } } false diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs index a5661e44af8a..1067f1e40ef6 100644 --- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs @@ -126,36 +126,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .statements .get(location.statement_index) .map(|stmt| &stmt.kind) + && let Some(local) = place.as_local() { - if let Some(local) = place.as_local() { - let local_decl = &self.body.local_decls[local]; - // opt_match_place is the - // match_span is the span of the expression being matched on - // match *x.y { ... } match_place is Some(*x.y) - // ^^^^ match_span is the span of *x.y - // - // opt_match_place is None for let [mut] x = ... statements, - // whether or not the right-hand side is a place expression - if let LocalInfo::User(BindingForm::Var(VarBindingForm { - opt_match_place: Some((opt_match_place, match_span)), - binding_mode: _, - opt_ty_info: _, - pat_span: _, - })) = *local_decl.local_info() - { - let stmt_source_info = self.body.source_info(location); - self.append_binding_error( - grouped_errors, - kind, - original_path, - *move_from, - local, - opt_match_place, - match_span, - stmt_source_info.span, - ); - return; - } + let local_decl = &self.body.local_decls[local]; + // opt_match_place is the + // match_span is the span of the expression being matched on + // match *x.y { ... } match_place is Some(*x.y) + // ^^^^ match_span is the span of *x.y + // + // opt_match_place is None for let [mut] x = ... statements, + // whether or not the right-hand side is a place expression + if let LocalInfo::User(BindingForm::Var(VarBindingForm { + opt_match_place: Some((opt_match_place, match_span)), + binding_mode: _, + opt_ty_info: _, + pat_span: _, + })) = *local_decl.local_info() + { + let stmt_source_info = self.body.source_info(location); + self.append_binding_error( + grouped_errors, + kind, + original_path, + *move_from, + local, + opt_match_place, + match_span, + stmt_source_info.span, + ); + return; } } diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs index edd14d155f66..517f9e88cd9b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs @@ -528,15 +528,15 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> { // match_adt_and_segment in this case. Res::Def(DefKind::TyAlias, _) => (), _ => { - if let Some(last_segment) = path.segments.last() { - if let Some(highlight) = self.match_adt_and_segment( + if let Some(last_segment) = path.segments.last() + && let Some(highlight) = self.match_adt_and_segment( args, needle_fr, last_segment, search_stack, - ) { - return Some(highlight); - } + ) + { + return Some(highlight); } } } diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5f1b655c6b60..68f1637e07ee 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -822,10 +822,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } - if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements { - if self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) { - continue; - } + if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements + && self.try_promote_type_test(infcx, type_test, propagated_outlives_requirements) + { + continue; } // Type-test failed. Report the error. @@ -1479,40 +1479,36 @@ impl<'tcx> RegionInferenceContext<'tcx> { shorter_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, ) -> RegionRelationCheckResult { - if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { + if let Some(propagated_outlives_requirements) = propagated_outlives_requirements // Shrink `longer_fr` until we find a non-local region (if we do). // We'll call it `fr-` -- it's ever so slightly smaller than // `longer_fr`. - if let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) - { - debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); + && let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr) + { + debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus); - let blame_span_category = self.find_outlives_blame_span( - longer_fr, - NllRegionVariableOrigin::FreeRegion, - shorter_fr, - ); + let blame_span_category = self.find_outlives_blame_span( + longer_fr, + NllRegionVariableOrigin::FreeRegion, + shorter_fr, + ); - // Grow `shorter_fr` until we find some non-local regions. (We - // always will.) We'll call them `shorter_fr+` -- they're ever - // so slightly larger than `shorter_fr`. - let shorter_fr_plus = - self.universal_region_relations.non_local_upper_bounds(shorter_fr); - debug!( - "try_propagate_universal_region_error: shorter_fr_plus={:?}", - shorter_fr_plus - ); - for fr in shorter_fr_plus { - // Push the constraint `fr-: shorter_fr+` - propagated_outlives_requirements.push(ClosureOutlivesRequirement { - subject: ClosureOutlivesSubject::Region(fr_minus), - outlived_free_region: fr, - blame_span: blame_span_category.1.span, - category: blame_span_category.0, - }); - } - return RegionRelationCheckResult::Propagated; + // Grow `shorter_fr` until we find some non-local regions. (We + // always will.) We'll call them `shorter_fr+` -- they're ever + // so slightly larger than `shorter_fr`. + let shorter_fr_plus = + self.universal_region_relations.non_local_upper_bounds(shorter_fr); + debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus); + for fr in shorter_fr_plus { + // Push the constraint `fr-: shorter_fr+` + propagated_outlives_requirements.push(ClosureOutlivesRequirement { + subject: ClosureOutlivesSubject::Region(fr_minus), + outlived_free_region: fr, + blame_span: blame_span_category.1.span, + category: blame_span_category.0, + }); } + return RegionRelationCheckResult::Propagated; } RegionRelationCheckResult::Error @@ -2085,11 +2081,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { let locations = self.scc_values.locations_outlived_by(scc); for location in locations { let bb = &body[location.block]; - if let Some(terminator) = &bb.terminator { + if let Some(terminator) = &bb.terminator // terminator of a loop should be TerminatorKind::FalseUnwind - if let TerminatorKind::FalseUnwind { .. } = terminator.kind { - return Some(location); - } + && let TerminatorKind::FalseUnwind { .. } = terminator.kind + { + return Some(location); } } None diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index d500088c259a..f363ef0a5a98 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -669,24 +669,24 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } - if let Some(annotation_index) = self.rvalue_user_ty(rv) { - if let Err(terr) = self.relate_type_and_user_type( + if let Some(annotation_index) = self.rvalue_user_ty(rv) + && let Err(terr) = self.relate_type_and_user_type( rv_ty, ty::Invariant, &UserTypeProjection { base: annotation_index, projs: vec![] }, location.to_locations(), ConstraintCategory::TypeAnnotation(AnnotationSource::GenericArg), - ) { - let annotation = &self.user_type_annotations[annotation_index]; - span_mirbug!( - self, - stmt, - "bad user type on rvalue ({:?} = {:?}): {:?}", - annotation, - rv_ty, - terr - ); - } + ) + { + let annotation = &self.user_type_annotations[annotation_index]; + span_mirbug!( + self, + stmt, + "bad user type on rvalue ({:?} = {:?}): {:?}", + annotation, + rv_ty, + terr + ); } if !self.unsized_feature_enabled() { diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 767835c34f02..657513991acd 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -505,10 +505,10 @@ fn thin_lto( // Save the current ThinLTO import information for the next compilation // session, overwriting the previous serialized data (if any). - if let Some(path) = key_map_path { - if let Err(err) = curr_key_map.save_to_file(&path) { - return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err })); - } + if let Some(path) = key_map_path + && let Err(err) = curr_key_map.save_to_file(&path) + { + return Err(write::llvm_err(dcx, LlvmError::WriteThinLtoKey { err })); } Ok((opt_jobs, copy_jobs)) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 0ade9edb0d2e..f712b3b83faa 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -687,10 +687,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { bx.nonnull_metadata(load); } - if let Some(pointee) = layout.pointee_info_at(bx, offset) { - if let Some(_) = pointee.safe { - bx.align_metadata(load, pointee.align); - } + if let Some(pointee) = layout.pointee_info_at(bx, offset) + && let Some(_) = pointee.safe + { + bx.align_metadata(load, pointee.align); } } abi::Primitive::Float(_) => {} diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 5ce301c0eb98..162fbf3d6e24 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3369,12 +3369,12 @@ fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box, did: LocalDefId) -> CodegenFnAttrs { ) .with_note("Rustc requires this item to have a specific mangled name.") .with_span_label(tcx.def_span(did), "should be the internal language item"); - if let Some(lang_item) = lang_item { - if let Some(link_name) = lang_item.link_name() { - err = err - .with_note("If you are trying to prevent mangling to ease debugging, many") - .with_note(format!( - "debuggers support a command such as `rbreak {link_name}` to" - )) - .with_note(format!( - "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" - )) - } + if let Some(lang_item) = lang_item + && let Some(link_name) = lang_item.link_name() + { + err = err + .with_note("If you are trying to prevent mangling to ease debugging, many") + .with_note(format!("debuggers support a command such as `rbreak {link_name}` to")) + .with_note(format!( + "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" + )) } err.emit(); } diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 0eb6f28bdb37..69637cadcc99 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -714,10 +714,10 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { fn visit_operand(&mut self, op: &Operand<'tcx>, location: Location) { self.super_operand(op, location); - if let Operand::Constant(c) = op { - if let Some(def_id) = c.check_static_ptr(self.tcx) { - self.check_static(def_id, self.span); - } + if let Operand::Constant(c) = op + && let Some(def_id) = c.check_static_ptr(self.tcx) + { + self.check_static(def_id, self.span); } } diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index b2e0577cc824..982e640fa92a 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -295,21 +295,18 @@ fn build_error_for_const_call<'tcx>( } let deref = "*".repeat(num_refs); - if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) { - if let Some(eq_idx) = call_str.find("==") { - if let Some(rhs_idx) = - call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) - { - let rhs_pos = - span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); - let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); - sugg = Some(errors::ConsiderDereferencing { - deref, - span: span.shrink_to_lo(), - rhs_span, - }); - } - } + if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) + && let Some(eq_idx) = call_str.find("==") + && let Some(rhs_idx) = + call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace()) + { + let rhs_pos = span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx); + let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos); + sugg = Some(errors::ConsiderDereferencing { + deref, + span: span.shrink_to_lo(), + rhs_span, + }); } } _ => {} diff --git a/compiler/rustc_const_eval/src/check_consts/resolver.rs b/compiler/rustc_const_eval/src/check_consts/resolver.rs index 9f7fcc509a58..d98e5027e4d6 100644 --- a/compiler/rustc_const_eval/src/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/check_consts/resolver.rs @@ -137,14 +137,14 @@ where // If a local with no projections is moved from (e.g. `x` in `y = x`), record that // it no longer needs to be dropped. - if let mir::Operand::Move(place) = operand { - if let Some(local) = place.as_local() { - // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier - // implementation we retain qualif if a local had been borrowed before. This might - // not be strictly necessary since the local is no longer initialized. - if !self.state.borrow.contains(local) { - self.state.qualif.remove(local); - } + if let mir::Operand::Move(place) = operand + && let Some(local) = place.as_local() + { + // For backward compatibility with the MaybeMutBorrowedLocals used in an earlier + // implementation we retain qualif if a local had been borrowed before. This might + // not be strictly necessary since the local is no longer initialized. + if !self.state.borrow.contains(local) { + self.state.qualif.remove(local); } } } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 0c888694e49b..0a8591ca140d 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -442,10 +442,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // # First compute the dynamic alignment // Packed type alignment needs to be capped. - if let ty::Adt(def, _) = layout.ty.kind() { - if let Some(packed) = def.repr().pack { - unsized_align = unsized_align.min(packed); - } + if let ty::Adt(def, _) = layout.ty.kind() + && let Some(packed) = def.repr().pack + { + unsized_align = unsized_align.min(packed); } // Choose max of two known alignments (combined value must diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 693b37829600..ed48f53c3105 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -320,10 +320,10 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { // for a coroutine). let var_hir_id = captured_place.get_root_variable(); let node = self.ecx.tcx.hir_node(var_hir_id); - if let hir::Node::Pat(pat) = node { - if let hir::PatKind::Binding(_, _, ident, _) = pat.kind { - name = Some(ident.name); - } + if let hir::Node::Pat(pat) = node + && let hir::PatKind::Binding(_, _, ident, _) = pat.kind + { + name = Some(ident.name); } } } diff --git a/compiler/rustc_errors/src/styled_buffer.rs b/compiler/rustc_errors/src/styled_buffer.rs index 790efd0286e9..095502e80aa5 100644 --- a/compiler/rustc_errors/src/styled_buffer.rs +++ b/compiler/rustc_errors/src/styled_buffer.rs @@ -153,12 +153,11 @@ impl StyledBuffer { /// 1. That line and column exist in `StyledBuffer` /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation` fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) { - if let Some(ref mut line) = self.lines.get_mut(line) { - if let Some(StyledChar { style: s, .. }) = line.get_mut(col) { - if overwrite || matches!(s, Style::NoStyle | Style::Quotation) { - *s = style; - } - } + if let Some(ref mut line) = self.lines.get_mut(line) + && let Some(StyledChar { style: s, .. }) = line.get_mut(col) + && (overwrite || matches!(s, Style::NoStyle | Style::Quotation)) + { + *s = style; } } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 0728b24eb142..afe79bed851c 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -530,13 +530,12 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> { .iter() .enumerate() .map(|(i, a)| { - if let hir::TyKind::Infer(()) = a.kind { - if let Some(suggested_ty) = + if let hir::TyKind::Infer(()) = a.kind + && let Some(suggested_ty) = self.lowerer().suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i)) - { - infer_replacements.push((a.span, suggested_ty.to_string())); - return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string()); - } + { + infer_replacements.push((a.span, suggested_ty.to_string())); + return Ty::new_error_with_message(tcx, a.span, suggested_ty.to_string()); } self.lowerer().lower_ty(a) diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index d7a827c649dd..7760642d8fb0 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -617,18 +617,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }); // Provide the resolved type of the associated constant to `type_of(AnonConst)`. - if let Some(const_arg) = constraint.ct() { - if let hir::ConstArgKind::Anon(anon_const) = const_arg.kind { - let ty = alias_term - .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); - let ty = check_assoc_const_binding_type( - self, - constraint.ident, - ty, - constraint.hir_id, - ); - tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); - } + if let Some(const_arg) = constraint.ct() + && let hir::ConstArgKind::Anon(anon_const) = const_arg.kind + { + let ty = alias_term + .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args)); + let ty = + check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id); + tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty)); } alias_term diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs index bf539dfab42a..3fddaee8cef4 100644 --- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs @@ -103,16 +103,15 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>( // over less-specific types (e.g. `Option>`) if self.depth >= self.cause_depth { self.cause = Some(error.obligation.cause); - if let hir::TyKind::TraitObject(..) = ty.kind { - if let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = + if let hir::TyKind::TraitObject(..) = ty.kind + && let DefKind::AssocTy | DefKind::AssocConst | DefKind::AssocFn = self.tcx.def_kind(self.def_id) - { - self.cause = Some(ObligationCause::new( - ty.span, - self.def_id, - ObligationCauseCode::DynCompatible(ty.span), - )); - } + { + self.cause = Some(ObligationCause::new( + ty.span, + self.def_id, + ObligationCauseCode::DynCompatible(ty.span), + )); } self.cause_depth = self.depth } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index eb8d671c939c..719989d57930 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2136,10 +2136,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) }; - if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { - if let Some(rslt) = check_in_progress(el) { - return rslt; - } + if let hir::ExprKind::If(_, _, Some(el)) = expr.kind + && let Some(rslt) = check_in_progress(el) + { + return rslt; } if let hir::ExprKind::Match(_, arms, _) = expr.kind { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d18f4e03d2fa..ede02fbf9bd3 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -615,31 +615,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { - if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { - // Report upto four upvars being captured to reduce the amount error messages - // reported back to the user. - let spans_and_labels = upvars - .iter() - .take(4) - .map(|(var_hir_id, upvar)| { - let var_name = self.tcx.hir_name(*var_hir_id).to_string(); - let msg = format!("`{var_name}` captured here"); - (upvar.span, msg) - }) - .collect::>(); + if let (ty::FnPtr(..), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) + && let Some(upvars) = self.tcx.upvars_mentioned(*def_id) + { + // Report upto four upvars being captured to reduce the amount error messages + // reported back to the user. + let spans_and_labels = upvars + .iter() + .take(4) + .map(|(var_hir_id, upvar)| { + let var_name = self.tcx.hir_name(*var_hir_id).to_string(); + let msg = format!("`{var_name}` captured here"); + (upvar.span, msg) + }) + .collect::>(); - let mut multi_span: MultiSpan = - spans_and_labels.iter().map(|(sp, _)| *sp).collect::>().into(); - for (sp, label) in spans_and_labels { - multi_span.push_span_label(sp, label); - } - err.span_note( - multi_span, - "closures can only be coerced to `fn` types if they do not capture any variables" - ); - return true; + let mut multi_span: MultiSpan = + spans_and_labels.iter().map(|(sp, _)| *sp).collect::>().into(); + for (sp, label) in spans_and_labels { + multi_span.push_span_label(sp, label); } + err.span_note( + multi_span, + "closures can only be coerced to `fn` types if they do not capture any variables", + ); + return true; } false } From bae38bad7803be7fdf1878188da9650f82548016 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 26 Jul 2025 06:21:54 +0500 Subject: [PATCH 110/201] use let chains in hir, lint, mir --- .../src/fn_ctxt/suggestions.rs | 12 +- .../rustc_hir_typeck/src/method/confirm.rs | 10 +- .../rustc_hir_typeck/src/typeck_root_ctxt.rs | 9 +- compiler/rustc_hir_typeck/src/writeback.rs | 12 +- compiler/rustc_lint/src/builtin.rs | 48 ++++---- compiler/rustc_lint/src/internal.rs | 31 +++-- compiler/rustc_lint/src/map_unit_fn.rs | 110 +++++++++--------- compiler/rustc_lint/src/non_fmt_panic.rs | 60 +++++----- compiler/rustc_lint/src/nonstandard_style.rs | 18 +-- compiler/rustc_lint/src/unused.rs | 53 ++++----- compiler/rustc_metadata/src/locator.rs | 9 +- compiler/rustc_metadata/src/rmeta/encoder.rs | 8 +- compiler/rustc_middle/src/middle/region.rs | 31 +++-- compiler/rustc_middle/src/ty/adt.rs | 8 +- compiler/rustc_middle/src/ty/layout.rs | 8 +- compiler/rustc_middle/src/ty/print/pretty.rs | 8 +- compiler/rustc_mir_build/src/thir/cx/block.rs | 38 +++--- compiler/rustc_mir_build/src/thir/cx/expr.rs | 18 +-- .../src/thir/pattern/check_match.rs | 12 +- .../src/drop_flag_effects.rs | 5 +- .../src/impls/initialized.rs | 13 +-- compiler/rustc_mir_dataflow/src/rustc_peek.rs | 48 ++++---- .../rustc_mir_dataflow/src/value_analysis.rs | 8 +- .../rustc_mir_transform/src/coroutine/drop.rs | 8 +- 24 files changed, 278 insertions(+), 307 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index ede02fbf9bd3..33ae4f6c45cf 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -3008,13 +3008,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns whether the given expression is an `else if`. fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::If(..) = expr.kind { - if let Node::Expr(hir::Expr { - kind: hir::ExprKind::If(_, _, Some(else_expr)), .. - }) = self.tcx.parent_hir_node(expr.hir_id) - { - return else_expr.hir_id == expr.hir_id; - } + if let hir::ExprKind::If(..) = expr.kind + && let Node::Expr(hir::Expr { kind: hir::ExprKind::If(_, _, Some(else_expr)), .. }) = + self.tcx.parent_hir_node(expr.hir_id) + { + return else_expr.hir_id == expr.hir_id; } false } diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 4c343bb7c229..8d9f7eaf1774 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -669,17 +669,17 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { fn check_for_illegal_method_calls(&self, pick: &probe::Pick<'_>) { // Disallow calls to the method `drop` defined in the `Drop` trait. - if let Some(trait_def_id) = pick.item.trait_container(self.tcx) { - if let Err(e) = callee::check_legal_trait_for_method_call( + if let Some(trait_def_id) = pick.item.trait_container(self.tcx) + && let Err(e) = callee::check_legal_trait_for_method_call( self.tcx, self.span, Some(self.self_expr.span), self.call_expr.span, trait_def_id, self.body_id.to_def_id(), - ) { - self.set_tainted_by_errors(e); - } + ) + { + self.set_tainted_by_errors(e); } } diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs index 9f4ab8ca5d41..6a985fc91e7d 100644 --- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs +++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs @@ -165,13 +165,12 @@ impl<'tcx> TypeckRootCtxt<'tcx> { if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) = obligation.predicate.kind().skip_binder() - { // If the projection predicate (Foo::Bar == X) has X as a non-TyVid, // we need to make it into one. - if let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) { - debug!("infer_var_info: {:?}.output = true", vid); - infer_var_info.entry(vid).or_default().output = true; - } + && let Some(vid) = predicate.term.as_type().and_then(|ty| ty.ty_vid()) + { + debug!("infer_var_info: {:?}.output = true", vid); + infer_var_info.entry(vid).or_default().output = true; } } } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index b2497cb0de16..093de950d636 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -227,21 +227,19 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); self.typeck_results.node_args_mut().remove(e.hir_id); - if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) { + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(base.hir_id) // Discard the need for a mutable borrow - // Extra adjustment made when indexing causes a drop // of size information - we need to get rid of it // Since this is "after" the other adjustment to be // discarded, we do an extra `pop()` - if let Some(Adjustment { + && let Some(Adjustment { kind: Adjust::Pointer(PointerCoercion::Unsize), .. }) = a.pop() - { - // So the borrow discard actually happens here - a.pop(); - } + { + // So the borrow discard actually happens here + a.pop(); } } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index eb4c3703dbd6..c48fb286dc0f 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2446,16 +2446,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { /// Determine if this expression is a "dangerous initialization". fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { - if let hir::ExprKind::Call(path_expr, args) = expr.kind { + if let hir::ExprKind::Call(path_expr, args) = expr.kind // Find calls to `mem::{uninitialized,zeroed}` methods. - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::mem_zeroed) => return Some(InitKind::Zeroed), - Some(sym::mem_uninitialized) => return Some(InitKind::Uninit), - Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed), - _ => {} - } + && let hir::ExprKind::Path(ref qpath) = path_expr.kind + { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::mem_zeroed) => return Some(InitKind::Zeroed), + Some(sym::mem_uninitialized) => return Some(InitKind::Uninit), + Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed), + _ => {} } } else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind { // Find problematic calls to `MaybeUninit::assume_init`. @@ -2463,14 +2463,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { // This is a call to *some* method named `assume_init`. // See if the `self` parameter is one of the dangerous constructors. - if let hir::ExprKind::Call(path_expr, _) = receiver.kind { - if let hir::ExprKind::Path(ref qpath) = path_expr.kind { - let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; - match cx.tcx.get_diagnostic_name(def_id) { - Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed), - Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit), - _ => {} - } + if let hir::ExprKind::Call(path_expr, _) = receiver.kind + && let hir::ExprKind::Path(ref qpath) = path_expr.kind + { + let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; + match cx.tcx.get_diagnostic_name(def_id) { + Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed), + Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit), + _ => {} } } } @@ -2724,13 +2724,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { } // check for call to `core::ptr::null` or `core::ptr::null_mut` hir::ExprKind::Call(path, _) => { - if let hir::ExprKind::Path(ref qpath) = path.kind { - if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { - return matches!( - cx.tcx.get_diagnostic_name(def_id), - Some(sym::ptr_null | sym::ptr_null_mut) - ); - } + if let hir::ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + { + return matches!( + cx.tcx.get_diagnostic_name(def_id), + Some(sym::ptr_null | sym::ptr_null_mut) + ); } } _ => {} diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index d8fc46aa9ab5..7dafcc199a32 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -411,22 +411,21 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]); impl EarlyLintPass for LintPassImpl { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind { - if let Some(last) = lint_pass.path.segments.last() { - if last.ident.name == sym::LintPass { - let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); - let call_site = expn_data.call_site; - if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) - && call_site.ctxt().outer_expn_data().kind - != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) - { - cx.emit_span_lint( - LINT_PASS_IMPL_WITHOUT_MACRO, - lint_pass.path.span, - LintPassByHand, - ); - } - } + if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind + && let Some(last) = lint_pass.path.segments.last() + && last.ident.name == sym::LintPass + { + let expn_data = lint_pass.path.span.ctxt().outer_expn_data(); + let call_site = expn_data.call_site; + if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass) + && call_site.ctxt().outer_expn_data().kind + != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass) + { + cx.emit_span_lint( + LINT_PASS_IMPL_WITHOUT_MACRO, + lint_pass.path.span, + LintPassByHand, + ); } } } diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index af509cb786db..a8803158889f 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -43,56 +43,50 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { return; } - if let StmtKind::Semi(expr) = stmt.kind { - if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind { - if path.ident.name.as_str() == "map" { - if receiver.span.from_expansion() - || args.iter().any(|e| e.span.from_expansion()) - || !is_impl_slice(cx, receiver) - || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap") - { - return; + if let StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(path, receiver, args, span) = expr.kind + { + if path.ident.name.as_str() == "map" { + if receiver.span.from_expansion() + || args.iter().any(|e| e.span.from_expansion()) + || !is_impl_slice(cx, receiver) + || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap") + { + return; + } + let arg_ty = cx.typeck_results().expr_ty(&args[0]); + let default_span = args[0].span; + if let ty::FnDef(id, _) = arg_ty.kind() { + let fn_ty = cx.tcx.fn_sig(id).skip_binder(); + let ret_ty = fn_ty.output().skip_binder(); + if is_unit_type(ret_ty) { + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), + argument_label: args[0].span, + map_label: span, + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } - let arg_ty = cx.typeck_results().expr_ty(&args[0]); - let default_span = args[0].span; - if let ty::FnDef(id, _) = arg_ty.kind() { - let fn_ty = cx.tcx.fn_sig(id).skip_binder(); - let ret_ty = fn_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx - .tcx - .span_of_impl(*id) - .unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } - } else if let ty::Closure(id, subs) = arg_ty.kind() { - let cl_ty = subs.as_closure().sig(); - let ret_ty = cl_ty.output().skip_binder(); - if is_unit_type(ret_ty) { - cx.emit_span_lint( - MAP_UNIT_FN, - span, - MappingToUnit { - function_label: cx - .tcx - .span_of_impl(*id) - .unwrap_or(default_span), - argument_label: args[0].span, - map_label: span, - suggestion: path.ident.span, - replace: "for_each".to_string(), - }, - ) - } + } else if let ty::Closure(id, subs) = arg_ty.kind() { + let cl_ty = subs.as_closure().sig(); + let ret_ty = cl_ty.output().skip_binder(); + if is_unit_type(ret_ty) { + cx.emit_span_lint( + MAP_UNIT_FN, + span, + MappingToUnit { + function_label: cx.tcx.span_of_impl(*id).unwrap_or(default_span), + argument_label: args[0].span, + map_label: span, + suggestion: path.ident.span, + replace: "for_each".to_string(), + }, + ) } } } @@ -101,10 +95,10 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { } fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { - if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - if let Some(impl_id) = cx.tcx.impl_of_method(method_id) { - return cx.tcx.type_of(impl_id).skip_binder().is_slice(); - } + if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + { + return cx.tcx.type_of(impl_id).skip_binder().is_slice(); } false } @@ -114,11 +108,11 @@ fn is_unit_type(ty: Ty<'_>) -> bool { } fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool { - if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) { - if let Some(item) = cx.tcx.get_diagnostic_name(def_id) { - if item.as_str() == name { - return true; - } + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) + && let Some(item) = cx.tcx.get_diagnostic_name(def_id) + { + if item.as_str() == name { + return true; } } false diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 16c061008085..2eabeeaa88f9 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -48,38 +48,38 @@ declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]); impl<'tcx> LateLintPass<'tcx> for NonPanicFmt { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Call(f, [arg]) = &expr.kind { - if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() { - let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id); + if let hir::ExprKind::Call(f, [arg]) = &expr.kind + && let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() + { + let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id); - if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic) - || cx.tcx.is_lang_item(def_id, LangItem::Panic) - || f_diagnostic_name == Some(sym::panic_str_2015) + if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic) + || cx.tcx.is_lang_item(def_id, LangItem::Panic) + || f_diagnostic_name == Some(sym::panic_str_2015) + { + if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { + if matches!( + cx.tcx.get_diagnostic_name(id), + Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro) + ) { + check_panic(cx, f, arg); + } + } + } else if f_diagnostic_name == Some(sym::unreachable_display) { + if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id + && cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) { - if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { - if matches!( - cx.tcx.get_diagnostic_name(id), - Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro) - ) { - check_panic(cx, f, arg); - } - } - } else if f_diagnostic_name == Some(sym::unreachable_display) { - if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id { - if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) { - check_panic( - cx, - f, - // This is safe because we checked above that the callee is indeed - // unreachable_display - match &arg.kind { - // Get the borrowed arg not the borrow - hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg, - _ => bug!("call to unreachable_display without borrow"), - }, - ); - } - } + check_panic( + cx, + f, + // This is safe because we checked above that the callee is indeed + // unreachable_display + match &arg.kind { + // Get the borrowed arg not the borrow + hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg, + _ => bug!("call to unreachable_display without borrow"), + }, + ); } } } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index db89396d1dc2..76e374deef6a 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -623,15 +623,15 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { .. }) = p.kind { - if let Res::Def(DefKind::Const, _) = path.res { - if let [segment] = path.segments { - NonUpperCaseGlobals::check_upper_case( - cx, - "constant in pattern", - None, - &segment.ident, - ); - } + if let Res::Def(DefKind::Const, _) = path.res + && let [segment] = path.segments + { + NonUpperCaseGlobals::check_upper_case( + cx, + "constant in pattern", + None, + &segment.ident, + ); } } } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a9eb1739f7f1..13dd6cb16618 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -562,20 +562,19 @@ declare_lint_pass!(PathStatements => [PATH_STATEMENTS]); impl<'tcx> LateLintPass<'tcx> for PathStatements { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - if let hir::StmtKind::Semi(expr) = s.kind { - if let hir::ExprKind::Path(_) = expr.kind { - let ty = cx.typeck_results().expr_ty(expr); - if ty.needs_drop(cx.tcx, cx.typing_env()) { - let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) - { - PathStatementDropSub::Suggestion { span: s.span, snippet } - } else { - PathStatementDropSub::Help { span: s.span } - }; - cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) + if let hir::StmtKind::Semi(expr) = s.kind + && let hir::ExprKind::Path(_) = expr.kind + { + let ty = cx.typeck_results().expr_ty(expr); + if ty.needs_drop(cx.tcx, cx.typing_env()) { + let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) { + PathStatementDropSub::Suggestion { span: s.span, snippet } } else { - cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); - } + PathStatementDropSub::Help { span: s.span } + }; + cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) + } else { + cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); } } } @@ -1509,21 +1508,19 @@ impl UnusedDelimLint for UnusedBraces { // let _: A<{produces_literal!()}>; // ``` // FIXME(const_generics): handle paths when #67075 is fixed. - if let [stmt] = inner.stmts.as_slice() { - if let ast::StmtKind::Expr(ref expr) = stmt.kind { - if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block) - && (ctx != UnusedDelimsCtx::AnonConst - || (matches!(expr.kind, ast::ExprKind::Lit(_)) - && !expr.span.from_expansion())) - && ctx != UnusedDelimsCtx::ClosureBody - && !cx.sess().source_map().is_multiline(value.span) - && value.attrs.is_empty() - && !value.span.from_expansion() - && !inner.span.from_expansion() - { - self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) - } - } + if let [stmt] = inner.stmts.as_slice() + && let ast::StmtKind::Expr(ref expr) = stmt.kind + && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block) + && (ctx != UnusedDelimsCtx::AnonConst + || (matches!(expr.kind, ast::ExprKind::Lit(_)) + && !expr.span.from_expansion())) + && ctx != UnusedDelimsCtx::ClosureBody + && !cx.sess().source_map().is_multiline(value.span) + && value.attrs.is_empty() + && !value.span.from_expansion() + && !inner.span.from_expansion() + { + self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } } ast::ExprKind::Let(_, ref expr, _, _) => { diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index c30cfd1fcf71..9fef22f9558d 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -370,12 +370,11 @@ impl<'a> CrateLocator<'a> { return self.find_commandline_library(crate_rejections); } let mut seen_paths = FxHashSet::default(); - if let Some(extra_filename) = self.extra_filename { - if let library @ Some(_) = + if let Some(extra_filename) = self.extra_filename + && let library @ Some(_) = self.find_library_crate(crate_rejections, extra_filename, &mut seen_paths)? - { - return Ok(library); - } + { + return Ok(library); } self.find_library_crate(crate_rejections, "", &mut seen_paths) } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 5cd98038fc6d..8a8ce2a789a6 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -2141,10 +2141,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { .push((id.owner_id.def_id.local_def_index, simplified_self_ty)); let trait_def = tcx.trait_def(trait_ref.def_id); - if let Ok(mut an) = trait_def.ancestors(tcx, def_id) { - if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) { - self.tables.impl_parent.set_some(def_id.index, parent.into()); - } + if let Ok(mut an) = trait_def.ancestors(tcx, def_id) + && let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) + { + self.tables.impl_parent.set_some(def_id.index, parent.into()); } // if this is an impl of `CoerceUnsized`, create its diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs index 0f5b63f5c1d2..800c1af660a4 100644 --- a/compiler/rustc_middle/src/middle/region.rs +++ b/compiler/rustc_middle/src/middle/region.rs @@ -175,23 +175,22 @@ impl Scope { return DUMMY_SP; }; let span = tcx.hir_span(hir_id); - if let ScopeData::Remainder(first_statement_index) = self.data { - if let Node::Block(blk) = tcx.hir_node(hir_id) { - // Want span for scope starting after the - // indexed statement and ending at end of - // `blk`; reuse span of `blk` and shift `lo` - // forward to end of indexed statement. - // - // (This is the special case alluded to in the - // doc-comment for this method) + if let ScopeData::Remainder(first_statement_index) = self.data + // Want span for scope starting after the + // indexed statement and ending at end of + // `blk`; reuse span of `blk` and shift `lo` + // forward to end of indexed statement. + // + // (This is the special case alluded to in the + // doc-comment for this method) + && let Node::Block(blk) = tcx.hir_node(hir_id) + { + let stmt_span = blk.stmts[first_statement_index.index()].span; - let stmt_span = blk.stmts[first_statement_index.index()].span; - - // To avoid issues with macro-generated spans, the span - // of the statement must be nested in that of the block. - if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { - return span.with_lo(stmt_span.lo()); - } + // To avoid issues with macro-generated spans, the span + // of the statement must be nested in that of the block. + if span.lo() <= stmt_span.lo() && stmt_span.lo() <= span.hi() { + return span.with_lo(stmt_span.lo()); } } span diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 275458fc85f8..3bf80d37e656 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -566,10 +566,10 @@ impl<'tcx> AdtDef<'tcx> { let mut prev_discr = None::>; self.variants().iter_enumerated().map(move |(i, v)| { let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx)); - if let VariantDiscr::Explicit(expr_did) = v.discr { - if let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) { - discr = new_discr; - } + if let VariantDiscr::Explicit(expr_did) = v.discr + && let Ok(new_discr) = self.eval_explicit_discr(tcx, expr_did) + { + discr = new_discr; } prev_discr = Some(discr); diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 809717513c79..a5123576fc64 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -1055,11 +1055,11 @@ where _ => Some(this), }; - if let Some(variant) = data_variant { + if let Some(variant) = data_variant // We're not interested in any unions. - if let FieldsShape::Union(_) = variant.fields { - data_variant = None; - } + && let FieldsShape::Union(_) = variant.fields + { + data_variant = None; } let mut result = None; diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 9ee64df0ad06..d1cc75699d2b 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -225,10 +225,10 @@ impl<'tcx> RegionHighlightMode<'tcx> { region: Option>, number: Option, ) { - if let Some(k) = region { - if let Some(n) = number { - self.highlighting_region(k, n); - } + if let Some(k) = region + && let Some(n) = number + { + self.highlighting_region(k, n); } } diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs index e858b629ab14..57ddb8eddb8e 100644 --- a/compiler/rustc_mir_build/src/thir/cx/block.rs +++ b/compiler/rustc_mir_build/src/thir/cx/block.rs @@ -76,28 +76,24 @@ impl<'tcx> ThirBuildCx<'tcx> { let mut pattern = self.pattern_from_hir(local.pat); debug!(?pattern); - if let Some(ty) = &local.ty { - if let Some(&user_ty) = + if let Some(ty) = &local.ty + && let Some(&user_ty) = self.typeck_results.user_provided_types().get(ty.hir_id) - { - debug!("mirror_stmts: user_ty={:?}", user_ty); - let annotation = CanonicalUserTypeAnnotation { - user_ty: Box::new(user_ty), - span: ty.span, - inferred_ty: self.typeck_results.node_type(ty.hir_id), - }; - pattern = Box::new(Pat { - ty: pattern.ty, - span: pattern.span, - kind: PatKind::AscribeUserType { - ascription: Ascription { - annotation, - variance: ty::Covariant, - }, - subpattern: pattern, - }, - }); - } + { + debug!("mirror_stmts: user_ty={:?}", user_ty); + let annotation = CanonicalUserTypeAnnotation { + user_ty: Box::new(user_ty), + span: ty.span, + inferred_ty: self.typeck_results.node_type(ty.hir_id), + }; + pattern = Box::new(Pat { + ty: pattern.ty, + span: pattern.span, + kind: PatKind::AscribeUserType { + ascription: Ascription { annotation, variance: ty::Covariant }, + subpattern: pattern, + }, + }); } let span = match local.init { diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b694409f327b..33bf4e3e29fb 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -105,11 +105,11 @@ impl<'tcx> ThirBuildCx<'tcx> { // // ^ error message points at this expression. // } let mut adjust_span = |expr: &mut Expr<'tcx>| { - if let ExprKind::Block { block } = expr.kind { - if let Some(last_expr) = self.thir[block].expr { - span = self.thir[last_expr].span; - expr.span = span; - } + if let ExprKind::Block { block } = expr.kind + && let Some(last_expr) = self.thir[block].expr + { + span = self.thir[last_expr].span; + expr.span = span; } }; @@ -956,10 +956,10 @@ impl<'tcx> ThirBuildCx<'tcx> { }; fn local(expr: &rustc_hir::Expr<'_>) -> Option { - if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind { - if let Res::Local(hir_id) = path.res { - return Some(hir_id); - } + if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind + && let Res::Local(hir_id) = path.res + { + return Some(hir_id); } None diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 7f47754f6bcd..ae67bb5075e8 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1155,14 +1155,12 @@ fn find_fallback_pattern_typo<'tcx>( } hir::Node::Block(hir::Block { stmts, .. }) => { for stmt in *stmts { - if let hir::StmtKind::Let(let_stmt) = stmt.kind { - if let hir::PatKind::Binding(_, _, binding_name, _) = + if let hir::StmtKind::Let(let_stmt) = stmt.kind + && let hir::PatKind::Binding(_, _, binding_name, _) = let_stmt.pat.kind - { - if name == binding_name.name { - lint.pattern_let_binding = Some(binding_name.span); - } - } + && name == binding_name.name + { + lint.pattern_let_binding = Some(binding_name.span); } } } diff --git a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs index c9c7fddae5a8..1402a1a8b913 100644 --- a/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs +++ b/compiler/rustc_mir_dataflow/src/drop_flag_effects.rs @@ -124,10 +124,9 @@ pub fn drop_flag_effects_for_location<'tcx, F>( // Drop does not count as a move but we should still consider the variable uninitialized. if let Some(Terminator { kind: TerminatorKind::Drop { place, .. }, .. }) = body.stmt_at(loc).right() + && let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) { - if let LookupResult::Exact(mpi) = move_data.rev_lookup.find(place.as_ref()) { - on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent)) - } + on_all_children_bits(move_data, mpi, |mpi| callback(mpi, DropFlagState::Absent)) } debug!("drop_flag_effects: assignment for location({:?})", loc); diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index 085757f0fb6e..117525eb7778 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -637,16 +637,13 @@ impl<'tcx> Analysis<'tcx> for EverInitializedPlaces<'_, 'tcx> { debug!("initializes move_indexes {:?}", init_loc_map[location]); state.gen_all(init_loc_map[location].iter().copied()); - if let mir::StatementKind::StorageDead(local) = stmt.kind { + if let mir::StatementKind::StorageDead(local) = stmt.kind // End inits for StorageDead, so that an immutable variable can // be reinitialized on the next iteration of the loop. - if let Some(move_path_index) = rev_lookup.find_local(local) { - debug!( - "clears the ever initialized status of {:?}", - init_path_map[move_path_index] - ); - state.kill_all(init_path_map[move_path_index].iter().copied()); - } + && let Some(move_path_index) = rev_lookup.find_local(local) + { + debug!("clears the ever initialized status of {:?}", init_path_map[move_path_index]); + state.kill_all(init_path_map[move_path_index].iter().copied()); } } diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index 303fc767b9a3..1682f3328574 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -135,12 +135,11 @@ fn value_assigned_to_local<'a, 'tcx>( stmt: &'a mir::Statement<'tcx>, local: Local, ) -> Option<&'a mir::Rvalue<'tcx>> { - if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind { - if let Some(l) = place.as_local() { - if local == l { - return Some(&*rvalue); - } - } + if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind + && let Some(l) = place.as_local() + && local == l + { + return Some(&*rvalue); } None @@ -178,31 +177,30 @@ impl PeekCall { let span = terminator.source_info.span; if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } = &terminator.kind + && let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() { - if let ty::FnDef(def_id, fn_args) = *func.const_.ty().kind() { - if tcx.intrinsic(def_id)?.name != sym::rustc_peek { - return None; - } + if tcx.intrinsic(def_id)?.name != sym::rustc_peek { + return None; + } - assert_eq!(fn_args.len(), 1); - let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0)); - let arg = match &args[0].node { - Operand::Copy(place) | Operand::Move(place) => { - if let Some(local) = place.as_local() { - local - } else { - tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); - return None; - } - } - _ => { + assert_eq!(fn_args.len(), 1); + let kind = PeekCallKind::from_arg_ty(fn_args.type_at(0)); + let arg = match &args[0].node { + Operand::Copy(place) | Operand::Move(place) => { + if let Some(local) = place.as_local() { + local + } else { tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); return None; } - }; + } + _ => { + tcx.dcx().emit_err(PeekMustBeNotTemporary { span }); + return None; + } + }; - return Some(PeekCall { arg, kind, span }); - } + return Some(PeekCall { arg, kind, span }); } None diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 83fd8ccba60e..005e79731307 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -215,10 +215,10 @@ impl State { // If both places are tracked, we copy the value to the target. // If the target is tracked, but the source is not, we do nothing, as invalidation has // already been performed. - if let Some(target_value) = map.places[target].value_index { - if let Some(source_value) = map.places[source].value_index { - values.insert(target_value, values.get(source_value).clone()); - } + if let Some(target_value) = map.places[target].value_index + && let Some(source_value) = map.places[source].value_index + { + values.insert(target_value, values.get(source_value).clone()); } for target_child in map.children(target) { // Try to find corresponding child and recurse. Reasoning is similar as above. diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs index 406575c4f43f..1a314e029f4a 100644 --- a/compiler/rustc_mir_transform/src/coroutine/drop.rs +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -23,10 +23,10 @@ impl<'tcx> MutVisitor<'tcx> for FixReturnPendingVisitor<'tcx> { } // Converting `_0 = Poll::::Pending` to `_0 = Poll::<()>::Pending` - if let Rvalue::Aggregate(kind, _) = rvalue { - if let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind { - *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); - } + if let Rvalue::Aggregate(kind, _) = rvalue + && let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind + { + *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); } } } From b8eb046e6ee3294969bf8faf31da226b0ea29d18 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Sat, 26 Jul 2025 06:22:20 +0500 Subject: [PATCH 111/201] use let chains in mir, resolve, target --- .../rustc_mir_transform/src/promote_consts.rs | 19 +- .../src/remove_noop_landing_pads.rs | 15 +- compiler/rustc_mir_transform/src/validate.rs | 17 +- .../rustc_monomorphize/src/partitioning.rs | 9 +- compiler/rustc_parse/src/parser/item.rs | 50 +++-- compiler/rustc_passes/src/reachable.rs | 8 +- compiler/rustc_passes/src/stability.rs | 16 +- compiler/rustc_passes/src/upvars.rs | 26 +-- compiler/rustc_privacy/src/lib.rs | 11 +- .../rustc_query_system/src/dep_graph/graph.rs | 12 +- compiler/rustc_query_system/src/query/job.rs | 8 +- .../rustc_resolve/src/late/diagnostics.rs | 204 +++++++++--------- compiler/rustc_resolve/src/macros.rs | 61 +++--- compiler/rustc_session/src/parse.rs | 8 +- compiler/rustc_session/src/session.rs | 10 +- compiler/rustc_span/src/source_map.rs | 8 +- .../rustc_target/src/callconv/loongarch.rs | 19 +- compiler/rustc_target/src/callconv/mips64.rs | 30 ++- compiler/rustc_target/src/callconv/mod.rs | 22 +- compiler/rustc_target/src/callconv/riscv.rs | 18 +- .../trait_impl_difference.rs | 5 +- .../src/error_reporting/infer/suggest.rs | 163 +++++++------- .../traits/on_unimplemented.rs | 18 +- .../src/error_reporting/traits/suggestions.rs | 36 ++-- .../src/traits/coherence.rs | 15 +- .../src/traits/select/mod.rs | 37 ++-- compiler/rustc_ty_utils/src/abi.rs | 10 +- compiler/rustc_ty_utils/src/ty.rs | 8 +- 28 files changed, 415 insertions(+), 448 deletions(-) diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 4e8f30e077b0..462ddfa3dd3c 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -997,12 +997,11 @@ fn promote_candidates<'tcx>( for candidate in candidates.into_iter().rev() { let Location { block, statement_index } = candidate.location; if let StatementKind::Assign(box (place, _)) = &body[block].statements[statement_index].kind + && let Some(local) = place.as_local() { - if let Some(local) = place.as_local() { - if temps[local] == TempState::PromotedOut { - // Already promoted. - continue; - } + if temps[local] == TempState::PromotedOut { + // Already promoted. + continue; } } @@ -1066,11 +1065,11 @@ fn promote_candidates<'tcx>( _ => true, }); let terminator = block.terminator_mut(); - if let TerminatorKind::Drop { place, target, .. } = &terminator.kind { - if let Some(index) = place.as_local() { - if promoted(index) { - terminator.kind = TerminatorKind::Goto { target: *target }; - } + if let TerminatorKind::Drop { place, target, .. } = &terminator.kind + && let Some(index) = place.as_local() + { + if promoted(index) { + terminator.kind = TerminatorKind::Goto { target: *target }; } } } diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs index 797056ad52d4..5b6d7ffb5110 100644 --- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs +++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs @@ -48,14 +48,13 @@ impl<'tcx> crate::MirPass<'tcx> for RemoveNoopLandingPads { let postorder: Vec<_> = traversal::postorder(body).map(|(bb, _)| bb).collect(); for bb in postorder { debug!(" processing {:?}", bb); - if let Some(unwind) = body[bb].terminator_mut().unwind_mut() { - if let UnwindAction::Cleanup(unwind_bb) = *unwind { - if nop_landing_pads.contains(unwind_bb) { - debug!(" removing noop landing pad"); - landing_pads_removed += 1; - *unwind = UnwindAction::Continue; - } - } + if let Some(unwind) = body[bb].terminator_mut().unwind_mut() + && let UnwindAction::Cleanup(unwind_bb) = *unwind + && nop_landing_pads.contains(unwind_bb) + { + debug!(" removing noop landing pad"); + landing_pads_removed += 1; + *unwind = UnwindAction::Continue; } body[bb].terminator_mut().successors_mut(|target| { diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 5860072d541f..98d12bf0a38b 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -80,15 +80,14 @@ impl<'tcx> crate::MirPass<'tcx> for Validator { cfg_checker.fail(location, msg); } - if let MirPhase::Runtime(_) = body.phase { - if let ty::InstanceKind::Item(_) = body.source.instance { - if body.has_free_regions() { - cfg_checker.fail( - Location::START, - format!("Free regions in optimized {} MIR", body.phase.name()), - ); - } - } + if let MirPhase::Runtime(_) = body.phase + && let ty::InstanceKind::Item(_) = body.source.instance + && body.has_free_regions() + { + cfg_checker.fail( + Location::START, + format!("Free regions in optimized {} MIR", body.phase.name()), + ); } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index ca8228de57e8..f3e7d582781d 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1178,12 +1178,11 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitio let autodiff_items = tcx.arena.alloc_from_iter(autodiff_items); // Output monomorphization stats per def_id - if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats { - if let Err(err) = + if let SwitchWithOptPath::Enabled(ref path) = tcx.sess.opts.unstable_opts.dump_mono_stats + && let Err(err) = dump_mono_items_stats(tcx, codegen_units, path, tcx.crate_name(LOCAL_CRATE)) - { - tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() }); - } + { + tcx.dcx().emit_fatal(CouldntDumpMonoStats { error: err.to_string() }); } if tcx.sess.opts.unstable_opts.print_mono_items { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index b767b0fcf994..65d84b3e3d9d 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1973,21 +1973,21 @@ impl<'a> Parser<'a> { format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)); // Try to recover extra trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind { - if let Some(last_segment) = segments.last() { - let guar = self.check_trailing_angle_brackets( - last_segment, - &[exp!(Comma), exp!(CloseBrace)], - ); - if let Some(_guar) = guar { - // Handle a case like `Vec>,` where we can continue parsing fields - // after the comma - let _ = self.eat(exp!(Comma)); + if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind + && let Some(last_segment) = segments.last() + { + let guar = self.check_trailing_angle_brackets( + last_segment, + &[exp!(Comma), exp!(CloseBrace)], + ); + if let Some(_guar) = guar { + // Handle a case like `Vec>,` where we can continue parsing fields + // after the comma + let _ = self.eat(exp!(Comma)); - // `check_trailing_angle_brackets` already emitted a nicer error, as - // proven by the presence of `_guar`. We can continue parsing. - return Ok(a_var); - } + // `check_trailing_angle_brackets` already emitted a nicer error, as + // proven by the presence of `_guar`. We can continue parsing. + return Ok(a_var); } } @@ -3034,18 +3034,16 @@ impl<'a> Parser<'a> { if let Ok(t) = &ty { // Check for trailing angle brackets - if let TyKind::Path(_, Path { segments, .. }) = &t.kind { - if let Some(segment) = segments.last() { - if let Some(guar) = - this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) - { - return Ok(( - dummy_arg(segment.ident, guar), - Trailing::No, - UsePreAttrPos::No, - )); - } - } + if let TyKind::Path(_, Path { segments, .. }) = &t.kind + && let Some(segment) = segments.last() + && let Some(guar) = + this.check_trailing_angle_brackets(segment, &[exp!(CloseParen)]) + { + return Ok(( + dummy_arg(segment.ident, guar), + Trailing::No, + UsePreAttrPos::No, + )); } if this.token != token::Comma && this.token != token::CloseParen { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index b49e8118fe37..2f78c22c7483 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -104,10 +104,10 @@ impl<'tcx> Visitor<'tcx> for ReachableContext<'tcx> { fn visit_inline_asm(&mut self, asm: &'tcx hir::InlineAsm<'tcx>, id: hir::HirId) { for (op, _) in asm.operands { - if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op { - if let Some(def_id) = def_id.as_local() { - self.reachable_symbols.insert(def_id); - } + if let hir::InlineAsmOperand::SymStatic { def_id, .. } = op + && let Some(def_id) = def_id.as_local() + { + self.reachable_symbols.insert(def_id); } } intravisit::walk_inline_asm(self, asm, id); diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 40999d622dc0..9ed7293873f5 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -929,10 +929,10 @@ struct CheckTraitImplStable<'tcx> { impl<'tcx> Visitor<'tcx> for CheckTraitImplStable<'tcx> { fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: hir::HirId) { - if let Some(def_id) = path.res.opt_def_id() { - if let Some(stab) = self.tcx.lookup_stability(def_id) { - self.fully_stable &= stab.level.is_stable(); - } + if let Some(def_id) = path.res.opt_def_id() + && let Some(stab) = self.tcx.lookup_stability(def_id) + { + self.fully_stable &= stab.level.is_stable(); } intravisit::walk_path(self, path) } @@ -1055,10 +1055,10 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) { // implications from this crate. remaining_implications.remove(&feature); - if let FeatureStability::Unstable { old_name: Some(alias) } = stability { - if let Some(span) = remaining_lib_features.swap_remove(&alias) { - tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); - } + if let FeatureStability::Unstable { old_name: Some(alias) } = stability + && let Some(span) = remaining_lib_features.swap_remove(&alias) + { + tcx.dcx().emit_err(errors::RenamedFeature { span, feature, alias }); } if remaining_lib_features.is_empty() && remaining_implications.is_empty() { diff --git a/compiler/rustc_passes/src/upvars.rs b/compiler/rustc_passes/src/upvars.rs index fae88fbba360..88f202919bb5 100644 --- a/compiler/rustc_passes/src/upvars.rs +++ b/compiler/rustc_passes/src/upvars.rs @@ -75,19 +75,19 @@ impl<'tcx> Visitor<'tcx> for CaptureCollector<'_, 'tcx> { } fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - if let hir::ExprKind::Closure(closure) = expr.kind { - if let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) { - // Every capture of a closure expression is a local in scope, - // that is moved/copied/borrowed into the closure value, and - // for this analysis they are like any other access to a local. - // - // E.g. in `|b| |c| (a, b, c)`, the upvars of the inner closure - // are `a` and `b`, and while `a` is not directly used in the - // outer closure, it needs to be an upvar there too, so that - // the inner closure can take it (from the outer closure's env). - for (&var_id, upvar) in upvars { - self.visit_local_use(var_id, upvar.span); - } + if let hir::ExprKind::Closure(closure) = expr.kind + && let Some(upvars) = self.tcx.upvars_mentioned(closure.def_id) + { + // Every capture of a closure expression is a local in scope, + // that is moved/copied/borrowed into the closure value, and + // for this analysis they are like any other access to a local. + // + // E.g. in `|b| |c| (a, b, c)`, the upvars of the inner closure + // are `a` and `b`, and while `a` is not directly used in the + // outer closure, it needs to be an upvar there too, so that + // the inner closure can take it (from the outer closure's env). + for (&var_id, upvar) in upvars { + self.visit_local_use(var_id, upvar.span); } } diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 6fd2b7fc12f0..b4fa11a8eb37 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -204,12 +204,10 @@ where // Something like `fn() {my_method}` type of the method // `impl Pub { pub fn my_method() {} }` is considered a private type, // so we need to visit the self type additionally. - if let Some(assoc_item) = tcx.opt_associated_item(def_id) { - if let Some(impl_def_id) = assoc_item.impl_container(tcx) { - try_visit!( - tcx.type_of(impl_def_id).instantiate_identity().visit_with(self) - ); - } + if let Some(assoc_item) = tcx.opt_associated_item(def_id) + && let Some(impl_def_id) = assoc_item.impl_container(tcx) + { + try_visit!(tcx.type_of(impl_def_id).instantiate_identity().visit_with(self)); } } ty::Alias(kind @ (ty::Inherent | ty::Free | ty::Projection), data) => { @@ -734,6 +732,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> { if let Some(ctor_def_id) = variant.data.ctor_def_id() { self.update(ctor_def_id, variant_ev, Level::Reachable); } + for field in variant.data.fields() { self.update(field.def_id, variant_ev, Level::Reachable); self.reach(field.def_id, variant_ev).ty(); diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 04fc32a9b500..7e258aaa54f7 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -498,12 +498,12 @@ impl DepGraph { #[cfg(debug_assertions)] { - if let Some(target) = task_deps.node { - if let Some(ref forbidden_edge) = data.current.forbidden_edge { - let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; - if forbidden_edge.test(&src, &target) { - panic!("forbidden edge {:?} -> {:?} created", src, target) - } + if let Some(target) = task_deps.node + && let Some(ref forbidden_edge) = data.current.forbidden_edge + { + let src = forbidden_edge.index_to_node.lock()[&dep_node_index]; + if forbidden_edge.test(&src, &target) { + panic!("forbidden edge {:?} -> {:?} created", src, target) } } } diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 7e61f5026da4..fd1ea997ebe5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -289,10 +289,10 @@ where F: FnMut(Span, QueryJobId) -> Option>, { // Visit the parent query which is a non-resumable waiter since it's on the same stack - if let Some(parent) = query.parent(query_map) { - if let Some(cycle) = visit(query.span(query_map), parent) { - return Some(cycle); - } + if let Some(parent) = query.parent(query_map) + && let Some(cycle) = visit(query.span(query_map), parent) + { + return Some(cycle); } // Visit the explicit waiters which use condvars and are resumable diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 69095942f524..1f78b65d07dd 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -818,10 +818,10 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // If the first argument in call is `self` suggest calling a method. if let Some((call_span, args_span)) = self.call_has_self_arg(source) { let mut args_snippet = String::new(); - if let Some(args_span) = args_span { - if let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span) { - args_snippet = snippet; - } + if let Some(args_span) = args_span + && let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(args_span) + { + args_snippet = snippet; } err.span_suggestion( @@ -955,59 +955,57 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Some(Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, _)), false, ) = (source, res, is_macro) - { - if let Some(bounds @ [first_bound, .., last_bound]) = + && let Some(bounds @ [first_bound, .., last_bound]) = self.diag_metadata.current_trait_object - { - fallback = true; - let spans: Vec = bounds - .iter() - .map(|bound| bound.span()) - .filter(|&sp| sp != base_error.span) - .collect(); + { + fallback = true; + let spans: Vec = bounds + .iter() + .map(|bound| bound.span()) + .filter(|&sp| sp != base_error.span) + .collect(); - let start_span = first_bound.span(); - // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) - let end_span = last_bound.span(); - // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) - let last_bound_span = spans.last().cloned().unwrap(); - let mut multi_span: MultiSpan = spans.clone().into(); - for sp in spans { - let msg = if sp == last_bound_span { - format!( - "...because of {these} bound{s}", - these = pluralize!("this", bounds.len() - 1), - s = pluralize!(bounds.len() - 1), - ) - } else { - String::new() - }; - multi_span.push_span_label(sp, msg); - } - multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); - err.span_help( - multi_span, - "`+` is used to constrain a \"trait object\" type with lifetimes or \ + let start_span = first_bound.span(); + // `end_span` is the end of the poly trait ref (Foo + 'baz + Bar><) + let end_span = last_bound.span(); + // `last_bound_span` is the last bound of the poly trait ref (Foo + >'baz< + Bar) + let last_bound_span = spans.last().cloned().unwrap(); + let mut multi_span: MultiSpan = spans.clone().into(); + for sp in spans { + let msg = if sp == last_bound_span { + format!( + "...because of {these} bound{s}", + these = pluralize!("this", bounds.len() - 1), + s = pluralize!(bounds.len() - 1), + ) + } else { + String::new() + }; + multi_span.push_span_label(sp, msg); + } + multi_span.push_span_label(base_error.span, "expected this type to be a trait..."); + err.span_help( + multi_span, + "`+` is used to constrain a \"trait object\" type with lifetimes or \ auto-traits; structs and enums can't be bound in that way", - ); - if bounds.iter().all(|bound| match bound { - ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, - ast::GenericBound::Trait(tr) => tr.span == base_error.span, - }) { - let mut sugg = vec![]; - if base_error.span != start_span { - sugg.push((start_span.until(base_error.span), String::new())); - } - if base_error.span != end_span { - sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); - } - - err.multipart_suggestion( - "if you meant to use a type and not a trait here, remove the bounds", - sugg, - Applicability::MaybeIncorrect, - ); + ); + if bounds.iter().all(|bound| match bound { + ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true, + ast::GenericBound::Trait(tr) => tr.span == base_error.span, + }) { + let mut sugg = vec![]; + if base_error.span != start_span { + sugg.push((start_span.until(base_error.span), String::new())); } + if base_error.span != end_span { + sugg.push((base_error.span.shrink_to_hi().to(end_span), String::new())); + } + + err.multipart_suggestion( + "if you meant to use a type and not a trait here, remove the bounds", + sugg, + Applicability::MaybeIncorrect, + ); } } @@ -1151,13 +1149,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } err.code(E0411); err.span_label(span, "`Self` is only available in impls, traits, and type definitions"); - if let Some(item) = self.diag_metadata.current_item { - if let Some(ident) = item.kind.ident() { - err.span_label( - ident.span, - format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()), - ); - } + if let Some(item) = self.diag_metadata.current_item + && let Some(ident) = item.kind.ident() + { + err.span_label( + ident.span, + format!("`Self` not allowed in {} {}", item.kind.article(), item.kind.descr()), + ); } true } @@ -1932,11 +1930,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor { - if let PathSource::Expr(Some(parent)) = source { - if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind { - bad_struct_syntax_suggestion(self, err, def_id); - return true; - } + if let PathSource::Expr(Some(parent)) = source + && let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind + { + bad_struct_syntax_suggestion(self, err, def_id); + return true; } struct_ctor } else { @@ -2344,19 +2342,13 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if filter_fn(Res::Local(ast::DUMMY_NODE_ID)) { if let Some(node_id) = self.diag_metadata.current_self_type.as_ref().and_then(extract_node_id) + && let Some(resolution) = self.r.partial_res_map.get(&node_id) + && let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = resolution.full_res() + && let Some(fields) = self.r.field_idents(did) + && let Some(field) = fields.iter().find(|id| ident.name == id.name) { // Look for a field with the same name in the current self_type. - if let Some(resolution) = self.r.partial_res_map.get(&node_id) { - if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) = - resolution.full_res() - { - if let Some(fields) = self.r.field_idents(did) { - if let Some(field) = fields.iter().find(|id| ident.name == id.name) { - return Some(AssocSuggestion::Field(field.span)); - } - } - } - } + return Some(AssocSuggestion::Field(field.span)); } } @@ -2391,44 +2383,44 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } // Look for associated items in the current trait. - if let Some((module, _)) = self.current_trait_ref { - if let Ok(binding) = self.r.maybe_resolve_ident_in_module( + if let Some((module, _)) = self.current_trait_ref + && let Ok(binding) = self.r.maybe_resolve_ident_in_module( ModuleOrUniformRoot::Module(module), ident, ns, &self.parent_scope, None, - ) { - let res = binding.res(); - if filter_fn(res) { - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => { - let has_self = match def_id.as_local() { - Some(def_id) => self - .r - .delegation_fn_sigs - .get(&def_id) - .is_some_and(|sig| sig.has_self), - None => { - self.r.tcx.fn_arg_idents(def_id).first().is_some_and(|&ident| { - matches!(ident, Some(Ident { name: kw::SelfLower, .. })) - }) - } - }; - if has_self { - return Some(AssocSuggestion::MethodWithSelf { called }); - } else { - return Some(AssocSuggestion::AssocFn { called }); + ) + { + let res = binding.res(); + if filter_fn(res) { + match res { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => { + let has_self = match def_id.as_local() { + Some(def_id) => self + .r + .delegation_fn_sigs + .get(&def_id) + .is_some_and(|sig| sig.has_self), + None => { + self.r.tcx.fn_arg_idents(def_id).first().is_some_and(|&ident| { + matches!(ident, Some(Ident { name: kw::SelfLower, .. })) + }) } + }; + if has_self { + return Some(AssocSuggestion::MethodWithSelf { called }); + } else { + return Some(AssocSuggestion::AssocFn { called }); } - Res::Def(DefKind::AssocConst, _) => { - return Some(AssocSuggestion::AssocConst); - } - Res::Def(DefKind::AssocTy, _) => { - return Some(AssocSuggestion::AssocType); - } - _ => {} } + Res::Def(DefKind::AssocConst, _) => { + return Some(AssocSuggestion::AssocConst); + } + Res::Def(DefKind::AssocTy, _) => { + return Some(AssocSuggestion::AssocType); + } + _ => {} } } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index f0225daa09db..1a7a8afe22dd 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -1023,40 +1023,39 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { node_id: NodeId, ) { let span = path.span; - if let Some(stability) = &ext.stability { - if let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } = + if let Some(stability) = &ext.stability + && let StabilityLevel::Unstable { reason, issue, is_soft, implied_by, .. } = stability.level - { - let feature = stability.feature; + { + let feature = stability.feature; - let is_allowed = - |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); - let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); - if !is_allowed(feature) && !allowed_by_implication { - let lint_buffer = &mut self.lint_buffer; - let soft_handler = |lint, span, msg: String| { - lint_buffer.buffer_lint( - lint, - node_id, - span, - BuiltinLintDiag::UnstableFeature( - // FIXME make this translatable - msg.into(), - ), - ) - }; - stability::report_unstable( - self.tcx.sess, - feature, - reason.to_opt_reason(), - issue, - None, - is_soft, + let is_allowed = + |feature| self.tcx.features().enabled(feature) || span.allows_unstable(feature); + let allowed_by_implication = implied_by.is_some_and(|feature| is_allowed(feature)); + if !is_allowed(feature) && !allowed_by_implication { + let lint_buffer = &mut self.lint_buffer; + let soft_handler = |lint, span, msg: String| { + lint_buffer.buffer_lint( + lint, + node_id, span, - soft_handler, - stability::UnstableKind::Regular, - ); - } + BuiltinLintDiag::UnstableFeature( + // FIXME make this translatable + msg.into(), + ), + ) + }; + stability::report_unstable( + self.tcx.sess, + feature, + reason.to_opt_reason(), + issue, + None, + is_soft, + span, + soft_handler, + stability::UnstableKind::Regular, + ); } } if let Some(depr) = &ext.deprecation { diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 9097b27b86c4..426480f0dbab 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -107,10 +107,10 @@ pub fn feature_err_issue( let span = span.into(); // Cancel an earlier warning for this same error, if it exists. - if let Some(span) = span.primary_span() { - if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) { - err.cancel() - } + if let Some(span) = span.primary_span() + && let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) + { + err.cancel() } let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() }); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 85bd8340c3cc..da00ccf0f71d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1362,11 +1362,11 @@ fn validate_commandline_args_with_session_available(sess: &Session) { sess.dcx().emit_err(errors::InstrumentationNotSupported { us: "XRay".to_string() }); } - if let Some(flavor) = sess.opts.cg.linker_flavor { - if let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) { - let flavor = flavor.desc(); - sess.dcx().emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list }); - } + if let Some(flavor) = sess.opts.cg.linker_flavor + && let Some(compatible_list) = sess.target.linker_flavor.check_compatibility(flavor) + { + let flavor = flavor.desc(); + sess.dcx().emit_err(errors::IncompatibleLinkerFlavor { flavor, compatible_list }); } if sess.opts.unstable_opts.function_return != FunctionReturn::default() { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index 8a3644163caf..d93151497986 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -826,10 +826,10 @@ impl SourceMap { /// Given a `Span`, tries to get a shorter span ending just after the first occurrence of `char` /// `c`. pub fn span_through_char(&self, sp: Span, c: char) -> Span { - if let Ok(snippet) = self.span_to_snippet(sp) { - if let Some(offset) = snippet.find(c) { - return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); - } + if let Ok(snippet) = self.span_to_snippet(sp) + && let Some(offset) = snippet.find(c) + { + return sp.with_hi(BytePos(sp.lo().0 + (offset + c.len_utf8()) as u32)); } sp } diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 27b41cc09ed0..d567ad401bb1 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -314,16 +314,15 @@ fn classify_arg<'a, Ty, C>( } fn extend_integer_width(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, _) = scalar.primitive() { - // 32-bit integers are always sign-extended - if i.size().bits() == 32 && xlen > 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, _) = scalar.primitive() + && i.size().bits() == 32 + && xlen > 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + // 32-bit integers are always sign-extended + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(xlen); diff --git a/compiler/rustc_target/src/callconv/mips64.rs b/compiler/rustc_target/src/callconv/mips64.rs index 77c0cf06fc16..0209838bec18 100644 --- a/compiler/rustc_target/src/callconv/mips64.rs +++ b/compiler/rustc_target/src/callconv/mips64.rs @@ -6,15 +6,14 @@ use crate::callconv::{ArgAbi, ArgExtension, CastTarget, FnAbi, PassMode, Uniform fn extend_integer_width_mips(arg: &mut ArgAbi<'_, Ty>, bits: u64) { // Always sign extend u32 values on 64-bit mips - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, signed) = scalar.primitive() { - if !signed && i.size().bits() == 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, signed) = scalar.primitive() + && !signed + && i.size().bits() == 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(bits); @@ -58,13 +57,12 @@ where ret.cast_to(reg); return; } - } else if ret.layout.fields.count() == 2 { - if let Some(reg0) = float_reg(cx, ret, 0) { - if let Some(reg1) = float_reg(cx, ret, 1) { - ret.cast_to(CastTarget::pair(reg0, reg1)); - return; - } - } + } else if ret.layout.fields.count() == 2 + && let Some(reg0) = float_reg(cx, ret, 0) + && let Some(reg1) = float_reg(cx, ret, 1) + { + ret.cast_to(CastTarget::pair(reg0, reg1)); + return; } } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index ab3271220eb4..63e56744aec9 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -495,18 +495,16 @@ impl<'a, Ty> ArgAbi<'a, Ty> { pub fn extend_integer_width_to(&mut self, bits: u64) { // Only integers have signedness - if let BackendRepr::Scalar(scalar) = self.layout.backend_repr { - if let Primitive::Int(i, signed) = scalar.primitive() { - if i.size().bits() < bits { - if let PassMode::Direct(ref mut attrs) = self.mode { - if signed { - attrs.ext(ArgExtension::Sext) - } else { - attrs.ext(ArgExtension::Zext) - }; - } - } - } + if let BackendRepr::Scalar(scalar) = self.layout.backend_repr + && let Primitive::Int(i, signed) = scalar.primitive() + && i.size().bits() < bits + && let PassMode::Direct(ref mut attrs) = self.mode + { + if signed { + attrs.ext(ArgExtension::Sext) + } else { + attrs.ext(ArgExtension::Zext) + }; } } diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index a06f54d60e7b..161e2c1645f9 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -393,16 +393,14 @@ fn classify_arg<'a, Ty, C>( } fn extend_integer_width(arg: &mut ArgAbi<'_, Ty>, xlen: u64) { - if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr { - if let Primitive::Int(i, _) = scalar.primitive() { - // 32-bit integers are always sign-extended - if i.size().bits() == 32 && xlen > 32 { - if let PassMode::Direct(ref mut attrs) = arg.mode { - attrs.ext(ArgExtension::Sext); - return; - } - } - } + if let BackendRepr::Scalar(scalar) = arg.layout.backend_repr + && let Primitive::Int(i, _) = scalar.primitive() + && i.size().bits() == 32 + && xlen > 32 + && let PassMode::Direct(ref mut attrs) = arg.mode + { + attrs.ext(ArgExtension::Sext); + return; } arg.extend_integer_width_to(xlen); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs index 772a7f013323..2a3268d3339e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/nice_region_error/trait_impl_difference.rs @@ -104,10 +104,9 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty::AssocKind::Fn { .. } => { if let Some(hir_id) = assoc_item.def_id.as_local().map(|id| self.tcx().local_def_id_to_hir_id(id)) + && let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id) { - if let Some(decl) = self.tcx().hir_fn_decl_by_hir_id(hir_id) { - visitor.visit_fn_decl(decl); - } + visitor.visit_fn_decl(decl); } } _ => {} diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index c0daf08ce079..44baa213b284 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -91,51 +91,51 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) { // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with // some modifications due to that being in typeck and this being in infer. - if let ObligationCauseCode::Pattern { .. } = cause.code() { - if let ty::Adt(expected_adt, args) = exp_found.expected.kind() { - let compatible_variants: Vec<_> = expected_adt - .variants() - .iter() - .filter(|variant| { - variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn) - }) - .filter_map(|variant| { - let sole_field = &variant.single_field(); - let sole_field_ty = sole_field.ty(self.tcx, args); - if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { - let variant_path = - with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); - // FIXME #56861: DRYer prelude filtering - if let Some(path) = variant_path.strip_prefix("std::prelude::") { - if let Some((_, path)) = path.split_once("::") { - return Some(path.to_string()); - } - } - Some(variant_path) - } else { - None + if let ObligationCauseCode::Pattern { .. } = cause.code() + && let ty::Adt(expected_adt, args) = exp_found.expected.kind() + { + let compatible_variants: Vec<_> = expected_adt + .variants() + .iter() + .filter(|variant| { + variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn) + }) + .filter_map(|variant| { + let sole_field = &variant.single_field(); + let sole_field_ty = sole_field.ty(self.tcx, args); + if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { + let variant_path = + with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); + // FIXME #56861: DRYer prelude filtering + if let Some(path) = variant_path.strip_prefix("std::prelude::") + && let Some((_, path)) = path.split_once("::") + { + return Some(path.to_string()); } - }) - .collect(); - match &compatible_variants[..] { - [] => {} - [variant] => { - let sugg = SuggestTuplePatternOne { - variant: variant.to_owned(), - span_low: cause.span.shrink_to_lo(), - span_high: cause.span.shrink_to_hi(), - }; - diag.subdiagnostic(sugg); - } - _ => { - // More than one matching variant. - let sugg = SuggestTuplePatternMany { - path: self.tcx.def_path_str(expected_adt.did()), - cause_span: cause.span, - compatible_variants, - }; - diag.subdiagnostic(sugg); + Some(variant_path) + } else { + None } + }) + .collect(); + match &compatible_variants[..] { + [] => {} + [variant] => { + let sugg = SuggestTuplePatternOne { + variant: variant.to_owned(), + span_low: cause.span.shrink_to_lo(), + span_high: cause.span.shrink_to_hi(), + }; + diag.subdiagnostic(sugg); + } + _ => { + // More than one matching variant. + let sugg = SuggestTuplePatternMany { + path: self.tcx.def_path_str(expected_adt.did()), + cause_span: cause.span, + compatible_variants, + }; + diag.subdiagnostic(sugg); } } } @@ -288,19 +288,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) .map(|field| (field.name, field.ty(self.tcx, expected_args))) .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) + && let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - let suggestion = if expected_def.is_struct() { - SuggestAccessingField::Safe { span, snippet, name, ty } - } else if expected_def.is_union() { - SuggestAccessingField::Unsafe { span, snippet, name, ty } - } else { - return; - }; - diag.subdiagnostic(suggestion); - } - } + let suggestion = if expected_def.is_struct() { + SuggestAccessingField::Safe { span, snippet, name, ty } + } else if expected_def.is_union() { + SuggestAccessingField::Unsafe { span, snippet, name, ty } + } else { + return; + }; + diag.subdiagnostic(suggestion); } } } @@ -540,38 +538,35 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ) -> Option { if let (ty::Adt(exp_def, exp_args), ty::Ref(_, found_ty, _)) = (expected.kind(), found.kind()) + && let ty::Adt(found_def, found_args) = *found_ty.kind() { - if let ty::Adt(found_def, found_args) = *found_ty.kind() { - if exp_def == &found_def { - let have_as_ref = &[ - (sym::Option, SuggestAsRefKind::Option), - (sym::Result, SuggestAsRefKind::Result), - ]; - if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { - self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) - }) { - let mut show_suggestion = true; - for (exp_ty, found_ty) in - std::iter::zip(exp_args.types(), found_args.types()) - { - match *exp_ty.kind() { - ty::Ref(_, exp_ty, _) => { - match (exp_ty.kind(), found_ty.kind()) { - (_, ty::Param(_)) - | (_, ty::Infer(_)) - | (ty::Param(_), _) - | (ty::Infer(_), _) => {} - _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} - _ => show_suggestion = false, - }; - } - ty::Param(_) | ty::Infer(_) => {} - _ => show_suggestion = false, + if exp_def == &found_def { + let have_as_ref = &[ + (sym::Option, SuggestAsRefKind::Option), + (sym::Result, SuggestAsRefKind::Result), + ]; + if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| { + self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg) + }) { + let mut show_suggestion = true; + for (exp_ty, found_ty) in std::iter::zip(exp_args.types(), found_args.types()) { + match *exp_ty.kind() { + ty::Ref(_, exp_ty, _) => { + match (exp_ty.kind(), found_ty.kind()) { + (_, ty::Param(_)) + | (_, ty::Infer(_)) + | (ty::Param(_), _) + | (ty::Infer(_), _) => {} + _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} + _ => show_suggestion = false, + }; } + ty::Param(_) | ty::Infer(_) => {} + _ => show_suggestion = false, } - if show_suggestion { - return Some(*msg); - } + } + if show_suggestion { + return Some(*msg); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 2344bc79f213..5765dfd891d4 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -381,15 +381,15 @@ impl IgnoredDiagnosticOption { old: Option, option_name: &'static str, ) { - if let (Some(new_item), Some(old_item)) = (new, old) { - if let Some(item_def_id) = item_def_id.as_local() { - tcx.emit_node_span_lint( - MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.local_def_id_to_hir_id(item_def_id), - new_item, - IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, - ); - } + if let (Some(new_item), Some(old_item)) = (new, old) + && let Some(item_def_id) = item_def_id.as_local() + { + tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.local_def_id_to_hir_id(item_def_id), + new_item, + IgnoredDiagnosticOption { span: new_item, prev_span: old_item, option_name }, + ); } } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index bf7d4257b626..c52487363664 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -2213,26 +2213,26 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { span: Span, trait_ref: DefId, ) { - if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) { - if let ty::AssocKind::Const { .. } | ty::AssocKind::Type { .. } = assoc_item.kind { - err.note(format!( - "{}s cannot be accessed directly on a `trait`, they can only be \ + if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) + && let ty::AssocKind::Const { .. } | ty::AssocKind::Type { .. } = assoc_item.kind + { + err.note(format!( + "{}s cannot be accessed directly on a `trait`, they can only be \ accessed through a specific `impl`", - self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) - )); + self.tcx.def_kind_descr(assoc_item.as_def_kind(), item_def_id) + )); - if !assoc_item.is_impl_trait_in_trait() { - err.span_suggestion_verbose( - span, - "use the fully qualified path to an implementation", - format!( - "::{}", - self.tcx.def_path_str(trait_ref), - assoc_item.name() - ), - Applicability::HasPlaceholders, - ); - } + if !assoc_item.is_impl_trait_in_trait() { + err.span_suggestion_verbose( + span, + "use the fully qualified path to an implementation", + format!( + "::{}", + self.tcx.def_path_str(trait_ref), + assoc_item.name() + ), + Applicability::HasPlaceholders, + ); } } } diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs index f50f01a285b5..07e78da37b3b 100644 --- a/compiler/rustc_trait_selection/src/traits/coherence.rs +++ b/compiler/rustc_trait_selection/src/traits/coherence.rs @@ -695,15 +695,14 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a, 'tcx> { source: CandidateSource::Impl(def_id), result: Ok(_), } = cand.kind() + && let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - if let ty::ImplPolarity::Reservation = infcx.tcx.impl_polarity(def_id) { - let message = infcx - .tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); - if let Some(message) = message { - self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); - } + let message = infcx + .tcx + .get_attr(def_id, sym::rustc_reservation_impl) + .and_then(|a| a.value_str()); + if let Some(message) = message { + self.causes.insert(IntercrateAmbiguityCause::ReservationImpl { message }); } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f90316f520b3..d7c3543cb3fd 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -781,16 +781,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self, &project_obligation, ) - { - if let Some(cached_res) = self + && let Some(cached_res) = self .infcx .inner .borrow_mut() .projection_cache() .is_complete(key) - { - break 'compute_res Ok(cached_res); - } + { + break 'compute_res Ok(cached_res); } // Need to explicitly set the depth of nested goals here as @@ -1436,24 +1434,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ) -> SelectionResult<'tcx, SelectionCandidate<'tcx>> { let tcx = self.tcx(); // Treat reservation impls as ambiguity. - if let ImplCandidate(def_id) = candidate { - if let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) { - if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { - let message = tcx - .get_attr(def_id, sym::rustc_reservation_impl) - .and_then(|a| a.value_str()); - if let Some(message) = message { - debug!( - "filter_reservation_impls: \ + if let ImplCandidate(def_id) = candidate + && let ty::ImplPolarity::Reservation = tcx.impl_polarity(def_id) + { + if let Some(intercrate_ambiguity_clauses) = &mut self.intercrate_ambiguity_causes { + let message = + tcx.get_attr(def_id, sym::rustc_reservation_impl).and_then(|a| a.value_str()); + if let Some(message) = message { + debug!( + "filter_reservation_impls: \ reservation impl ambiguity on {:?}", - def_id - ); - intercrate_ambiguity_clauses - .insert(IntercrateAmbiguityCause::ReservationImpl { message }); - } + def_id + ); + intercrate_ambiguity_clauses + .insert(IntercrateAmbiguityCause::ReservationImpl { message }); } - return Ok(None); } + return Ok(None); } Ok(Some(candidate)) } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index f0ff50318abc..af2e000e3402 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -642,11 +642,11 @@ fn fn_abi_adjust_for_abi<'tcx>( // The `deduced_param_attrs` list could be empty if this is a type of function // we can't deduce any parameters for, so make sure the argument index is in // bounds. - if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) { - if deduced_param_attrs.read_only { - attrs.regular.insert(ArgAttribute::ReadOnly); - debug!("added deduced read-only attribute"); - } + if let Some(deduced_param_attrs) = deduced_param_attrs.get(arg_idx) + && deduced_param_attrs.read_only + { + attrs.regular.insert(ArgAttribute::ReadOnly); + debug!("added deduced read-only attribute"); } } } diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index f8d793464a93..8e09a4a8996d 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -106,10 +106,10 @@ fn adt_sizedness_constraint<'tcx>( tcx: TyCtxt<'tcx>, (def_id, sizedness): (DefId, SizedTraitKind), ) -> Option>> { - if let Some(def_id) = def_id.as_local() { - if let ty::Representability::Infinite(_) = tcx.representability(def_id) { - return None; - } + if let Some(def_id) = def_id.as_local() + && let ty::Representability::Infinite(_) = tcx.representability(def_id) + { + return None; } let def = tcx.adt_def(def_id); From dc88902f59b30092e2408648c1899c84c870476e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 28 Jul 2025 09:01:54 +0300 Subject: [PATCH 112/201] Adjust triagebot config for rustc-josh-sync --- src/tools/rust-analyzer/triagebot.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/triagebot.toml b/src/tools/rust-analyzer/triagebot.toml index 2201b5a5e7c9..c9862495bc0c 100644 --- a/src/tools/rust-analyzer/triagebot.toml +++ b/src/tools/rust-analyzer/triagebot.toml @@ -17,6 +17,7 @@ exclude_titles = [ # exclude syncs from subtree in rust-lang/rust "sync from downstream", "Sync from rust", "sync from rust", + "Rustc pull update", ] labels = ["has-merge-commits", "S-waiting-on-author"] From 173926da2bd94bde24740a4b9e6b1bac1bfcb910 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Tue, 1 Jul 2025 09:33:35 -0700 Subject: [PATCH 113/201] Remove `[T]::array_chunks(_mut)` --- ...sroot_tests-128bit-atomic-operations.patch | 2 +- library/alloc/src/lib.rs | 1 - library/alloc/src/slice.rs | 4 - library/alloc/src/string.rs | 24 +- library/core/src/slice/iter.rs | 249 ------------------ library/core/src/slice/mod.rs | 76 ------ library/core/src/str/iter.rs | 2 +- library/coretests/tests/lib.rs | 1 - library/coretests/tests/slice.rs | 184 ------------- library/std/src/lib.rs | 1 - library/std/src/sys/random/sgx.rs | 14 +- library/std/src/sys/random/uefi.rs | 5 +- 12 files changed, 23 insertions(+), 540 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch index f6e6bbc2387c..f3d1d5c43ea1 100644 --- a/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch +++ b/compiler/rustc_codegen_cranelift/patches/0027-sysroot_tests-128bit-atomic-operations.patch @@ -19,7 +19,7 @@ index 1e336bf..35e6f54 100644 -#![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] - #![feature(array_chunks)] + #![feature(array_ptr_get)] diff --git a/coretests/tests/atomic.rs b/coretests/tests/atomic.rs index b735957..ea728b6 100644 --- a/coretests/tests/atomic.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 6b6e4df4cba7..c091e496c509 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -94,7 +94,6 @@ // tidy-alphabetical-start #![feature(alloc_layout_extra)] #![feature(allocator_api)] -#![feature(array_chunks)] #![feature(array_into_iter_constructors)] #![feature(array_windows)] #![feature(ascii_char)] diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index b4da56578c89..ce9f967cc387 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -16,10 +16,6 @@ use core::cmp::Ordering::{self, Less}; use core::mem::MaybeUninit; #[cfg(not(no_global_oom_handling))] use core::ptr; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use core::slice::ArrayChunks; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use core::slice::ArrayChunksMut; #[unstable(feature = "array_windows", issue = "75027")] pub use core::slice::ArrayWindows; #[stable(feature = "inherent_ascii_escape", since = "1.60.0")] diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs index a189c00a6b61..d58240f3051e 100644 --- a/library/alloc/src/string.rs +++ b/library/alloc/src/string.rs @@ -787,12 +787,12 @@ impl String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "str_from_utf16_endian", issue = "116258")] pub fn from_utf16le(v: &[u8]) -> Result { - if v.len() % 2 != 0 { + let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error(())); - } + }; match (cfg!(target_endian = "little"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), - _ => char::decode_utf16(v.array_chunks::<2>().copied().map(u16::from_le_bytes)) + _ => char::decode_utf16(chunks.iter().copied().map(u16::from_le_bytes)) .collect::>() .map_err(|_| FromUtf16Error(())), } @@ -830,11 +830,11 @@ impl String { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", _ => { - let mut iter = v.array_chunks::<2>(); - let string = char::decode_utf16(iter.by_ref().copied().map(u16::from_le_bytes)) + let (chunks, remainder) = v.as_chunks::<2>(); + let string = char::decode_utf16(chunks.iter().copied().map(u16::from_le_bytes)) .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) .collect(); - if iter.remainder().is_empty() { string } else { string + "\u{FFFD}" } + if remainder.is_empty() { string } else { string + "\u{FFFD}" } } } } @@ -862,12 +862,12 @@ impl String { #[cfg(not(no_global_oom_handling))] #[unstable(feature = "str_from_utf16_endian", issue = "116258")] pub fn from_utf16be(v: &[u8]) -> Result { - if v.len() % 2 != 0 { + let (chunks, []) = v.as_chunks::<2>() else { return Err(FromUtf16Error(())); - } + }; match (cfg!(target_endian = "big"), unsafe { v.align_to::() }) { (true, ([], v, [])) => Self::from_utf16(v), - _ => char::decode_utf16(v.array_chunks::<2>().copied().map(u16::from_be_bytes)) + _ => char::decode_utf16(chunks.iter().copied().map(u16::from_be_bytes)) .collect::>() .map_err(|_| FromUtf16Error(())), } @@ -905,11 +905,11 @@ impl String { (true, ([], v, [])) => Self::from_utf16_lossy(v), (true, ([], v, [_remainder])) => Self::from_utf16_lossy(v) + "\u{FFFD}", _ => { - let mut iter = v.array_chunks::<2>(); - let string = char::decode_utf16(iter.by_ref().copied().map(u16::from_be_bytes)) + let (chunks, remainder) = v.as_chunks::<2>(); + let string = char::decode_utf16(chunks.iter().copied().map(u16::from_be_bytes)) .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) .collect(); - if iter.remainder().is_empty() { string } else { string + "\u{FFFD}" } + if remainder.is_empty() { string } else { string + "\u{FFFD}" } } } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index 33132dcc7148..ae910e052520 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2301,255 +2301,6 @@ impl ExactSizeIterator for ArrayWindows<'_, T, N> { } } -/// An iterator over a slice in (non-overlapping) chunks (`N` elements at a -/// time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `N-1` elements will be omitted but can be retrieved from -/// the [`remainder`] function from the iterator. -/// -/// This struct is created by the [`array_chunks`] method on [slices]. -/// -/// # Example -/// -/// ``` -/// #![feature(array_chunks)] -/// -/// let slice = ['l', 'o', 'r', 'e', 'm']; -/// let mut iter = slice.array_chunks::<2>(); -/// assert_eq!(iter.next(), Some(&['l', 'o'])); -/// assert_eq!(iter.next(), Some(&['r', 'e'])); -/// assert_eq!(iter.next(), None); -/// ``` -/// -/// [`array_chunks`]: slice::array_chunks -/// [`remainder`]: ArrayChunks::remainder -/// [slices]: slice -#[derive(Debug)] -#[unstable(feature = "array_chunks", issue = "74985")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct ArrayChunks<'a, T: 'a, const N: usize> { - iter: Iter<'a, [T; N]>, - rem: &'a [T], -} - -impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - pub(super) const fn new(slice: &'a [T]) -> Self { - let (array_slice, rem) = slice.as_chunks(); - Self { iter: array_slice.iter(), rem } - } - - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `N-1` - /// elements. - #[must_use] - #[unstable(feature = "array_chunks", issue = "74985")] - pub fn remainder(&self) -> &'a [T] { - self.rem - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[unstable(feature = "array_chunks", issue = "74985")] -impl Clone for ArrayChunks<'_, T, N> { - fn clone(&self) -> Self { - ArrayChunks { iter: self.iter.clone(), rem: self.rem } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> Iterator for ArrayChunks<'a, T, N> { - type Item = &'a [T; N]; - - #[inline] - fn next(&mut self) -> Option<&'a [T; N]> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a [T; N] { - // SAFETY: The safety guarantees of `__iterator_get_unchecked` are - // transferred to the caller. - unsafe { self.iter.__iterator_get_unchecked(i) } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunks<'a, T, N> { - #[inline] - fn next_back(&mut self) -> Option<&'a [T; N]> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.iter.nth_back(n) - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl ExactSizeIterator for ArrayChunks<'_, T, N> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ArrayChunks<'_, T, N> {} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl FusedIterator for ArrayChunks<'_, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunks<'a, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunks<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; -} - -/// An iterator over a slice in (non-overlapping) mutable chunks (`N` elements -/// at a time), starting at the beginning of the slice. -/// -/// When the slice len is not evenly divided by the chunk size, the last -/// up to `N-1` elements will be omitted but can be retrieved from -/// the [`into_remainder`] function from the iterator. -/// -/// This struct is created by the [`array_chunks_mut`] method on [slices]. -/// -/// # Example -/// -/// ``` -/// #![feature(array_chunks)] -/// -/// let mut slice = ['l', 'o', 'r', 'e', 'm']; -/// let iter = slice.array_chunks_mut::<2>(); -/// ``` -/// -/// [`array_chunks_mut`]: slice::array_chunks_mut -/// [`into_remainder`]: ../../std/slice/struct.ArrayChunksMut.html#method.into_remainder -/// [slices]: slice -#[derive(Debug)] -#[unstable(feature = "array_chunks", issue = "74985")] -#[must_use = "iterators are lazy and do nothing unless consumed"] -pub struct ArrayChunksMut<'a, T: 'a, const N: usize> { - iter: IterMut<'a, [T; N]>, - rem: &'a mut [T], -} - -impl<'a, T, const N: usize> ArrayChunksMut<'a, T, N> { - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - pub(super) const fn new(slice: &'a mut [T]) -> Self { - let (array_slice, rem) = slice.as_chunks_mut(); - Self { iter: array_slice.iter_mut(), rem } - } - - /// Returns the remainder of the original slice that is not going to be - /// returned by the iterator. The returned slice has at most `N-1` - /// elements. - #[must_use = "`self` will be dropped if the result is not used"] - #[unstable(feature = "array_chunks", issue = "74985")] - pub fn into_remainder(self) -> &'a mut [T] { - self.rem - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> Iterator for ArrayChunksMut<'a, T, N> { - type Item = &'a mut [T; N]; - - #[inline] - fn next(&mut self) -> Option<&'a mut [T; N]> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } - - #[inline] - fn count(self) -> usize { - self.iter.count() - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.iter.nth(n) - } - - #[inline] - fn last(self) -> Option { - self.iter.last() - } - - unsafe fn __iterator_get_unchecked(&mut self, i: usize) -> &'a mut [T; N] { - // SAFETY: The safety guarantees of `__iterator_get_unchecked` are transferred to - // the caller. - unsafe { self.iter.__iterator_get_unchecked(i) } - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl<'a, T, const N: usize> DoubleEndedIterator for ArrayChunksMut<'a, T, N> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut [T; N]> { - self.iter.next_back() - } - - #[inline] - fn nth_back(&mut self, n: usize) -> Option { - self.iter.nth_back(n) - } -} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl ExactSizeIterator for ArrayChunksMut<'_, T, N> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[unstable(feature = "trusted_len", issue = "37572")] -unsafe impl TrustedLen for ArrayChunksMut<'_, T, N> {} - -#[unstable(feature = "array_chunks", issue = "74985")] -impl FusedIterator for ArrayChunksMut<'_, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccess for ArrayChunksMut<'a, T, N> {} - -#[doc(hidden)] -#[unstable(feature = "array_chunks", issue = "74985")] -unsafe impl<'a, T, const N: usize> TrustedRandomAccessNoCoerce for ArrayChunksMut<'a, T, N> { - const MAY_HAVE_SIDE_EFFECT: bool = false; -} - /// An iterator over a slice in (non-overlapping) chunks (`chunk_size` elements at a /// time), starting at the end of the slice. /// diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fe5affc48be..1f34a7931d24 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -52,8 +52,6 @@ pub use index::SliceIndex; pub use index::{range, try_range}; #[unstable(feature = "array_windows", issue = "75027")] pub use iter::ArrayWindows; -#[unstable(feature = "array_chunks", issue = "74985")] -pub use iter::{ArrayChunks, ArrayChunksMut}; #[stable(feature = "slice_group_by", since = "1.77.0")] pub use iter::{ChunkBy, ChunkByMut}; #[stable(feature = "rust1", since = "1.0.0")] @@ -1448,42 +1446,6 @@ impl [T] { (remainder, array_slice) } - /// Returns an iterator over `N` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are array references and do not overlap. If `N` does not divide the - /// length of the slice, then the last up to `N-1` elements will be omitted and can be - /// retrieved from the `remainder` function of the iterator. - /// - /// This method is the const generic equivalent of [`chunks_exact`]. - /// - /// # Panics - /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. - /// - /// # Examples - /// - /// ``` - /// #![feature(array_chunks)] - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.array_chunks(); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert!(iter.next().is_none()); - /// assert_eq!(iter.remainder(), &['m']); - /// ``` - /// - /// [`chunks_exact`]: slice::chunks_exact - #[unstable(feature = "array_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - #[track_caller] - pub const fn array_chunks(&self) -> ArrayChunks<'_, T, N> { - assert!(N != 0, "chunk size must be non-zero"); - ArrayChunks::new(self) - } - /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// @@ -1646,44 +1608,6 @@ impl [T] { (remainder, array_slice) } - /// Returns an iterator over `N` elements of the slice at a time, starting at the - /// beginning of the slice. - /// - /// The chunks are mutable array references and do not overlap. If `N` does not divide - /// the length of the slice, then the last up to `N-1` elements will be omitted and - /// can be retrieved from the `into_remainder` function of the iterator. - /// - /// This method is the const generic equivalent of [`chunks_exact_mut`]. - /// - /// # Panics - /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. - /// - /// # Examples - /// - /// ``` - /// #![feature(array_chunks)] - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.array_chunks_mut() { - /// *chunk = [count; 2]; - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 0]); - /// ``` - /// - /// [`chunks_exact_mut`]: slice::chunks_exact_mut - #[unstable(feature = "array_chunks", issue = "74985")] - #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - #[inline] - #[track_caller] - pub const fn array_chunks_mut(&mut self) -> ArrayChunksMut<'_, T, N> { - assert!(N != 0, "chunk size must be non-zero"); - ArrayChunksMut::new(self) - } - /// Returns an iterator over overlapping windows of `N` elements of a slice, /// starting at the beginning of the slice. /// diff --git a/library/core/src/str/iter.rs b/library/core/src/str/iter.rs index bcf886484add..d2985d8a1866 100644 --- a/library/core/src/str/iter.rs +++ b/library/core/src/str/iter.rs @@ -52,7 +52,7 @@ impl<'a> Iterator for Chars<'a> { const CHUNK_SIZE: usize = 32; if remainder >= CHUNK_SIZE { - let mut chunks = self.iter.as_slice().array_chunks::(); + let mut chunks = self.iter.as_slice().as_chunks::().0.iter(); let mut bytes_skipped: usize = 0; while remainder > CHUNK_SIZE diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c5bfd1574e29..57a89365fa98 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -2,7 +2,6 @@ #![cfg_attr(target_has_atomic = "128", feature(integer_atomics))] #![cfg_attr(test, feature(cfg_select))] #![feature(alloc_layout_extra)] -#![feature(array_chunks)] #![feature(array_ptr_get)] #![feature(array_try_from_fn)] #![feature(array_windows)] diff --git a/library/coretests/tests/slice.rs b/library/coretests/tests/slice.rs index d17e681480c7..992f24cb18f2 100644 --- a/library/coretests/tests/slice.rs +++ b/library/coretests/tests/slice.rs @@ -611,190 +611,6 @@ fn test_chunks_exact_mut_zip() { assert_eq!(v1, [13, 14, 19, 20, 4]); } -#[test] -fn test_array_chunks_infer() { - let v: &[i32] = &[0, 1, 2, 3, 4, -4]; - let c = v.array_chunks(); - for &[a, b, c] in c { - assert_eq!(a + b + c, 3); - } - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let total = v2.array_chunks().map(|&[a, b]| a * b).sum::(); - assert_eq!(total, 2 * 3 + 4 * 5); -} - -#[test] -fn test_array_chunks_count() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<3>(); - assert_eq!(c.count(), 2); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &[i32] = &[]; - let c3 = v3.array_chunks::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_nth() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_nth_back() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &[i32] = &[0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_last() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - let c = v.array_chunks::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &[i32] = &[0, 1, 2, 3, 4]; - let c2 = v2.array_chunks::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_remainder() { - let v: &[i32] = &[0, 1, 2, 3, 4]; - let c = v.array_chunks::<2>(); - assert_eq!(c.remainder(), &[4]); -} - -#[test] -fn test_array_chunks_zip() { - let v1: &[i32] = &[0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - let res = v1 - .array_chunks::<2>() - .zip(v2.array_chunks::<2>()) - .map(|(a, b)| a.iter().sum::() + b.iter().sum::()) - .collect::>(); - assert_eq!(res, vec![14, 22]); -} - -#[test] -fn test_array_chunks_mut_infer() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - for a in v.array_chunks_mut() { - let sum = a.iter().sum::(); - *a = [sum; 3]; - } - assert_eq!(v, &[3, 3, 3, 12, 12, 12, 6]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - v2.array_chunks_mut().for_each(|[a, b]| core::mem::swap(a, b)); - assert_eq!(v2, &[1, 0, 3, 2, 5, 4, 6]); -} - -#[test] -fn test_array_chunks_mut_count() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<3>(); - assert_eq!(c.count(), 2); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.count(), 2); - - let v3: &mut [i32] = &mut []; - let c3 = v3.array_chunks_mut::<2>(); - assert_eq!(c3.count(), 0); -} - -#[test] -fn test_array_chunks_mut_nth() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4, 5, 6]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth(1).unwrap(), &[3, 4, 5]); - assert_eq!(c2.next(), None); -} - -#[test] -fn test_array_chunks_mut_nth_back() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let mut c = v.array_chunks_mut::<2>(); - assert_eq!(c.nth_back(1).unwrap(), &[2, 3]); - assert_eq!(c.next().unwrap(), &[0, 1]); - assert_eq!(c.next(), None); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c2 = v2.array_chunks_mut::<3>(); - assert_eq!(c2.nth_back(0).unwrap(), &[0, 1, 2]); - assert_eq!(c2.next(), None); - assert_eq!(c2.next_back(), None); - - let v3: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let mut c3 = v3.array_chunks_mut::<10>(); - assert_eq!(c3.nth_back(0), None); -} - -#[test] -fn test_array_chunks_mut_last() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.last().unwrap(), &[4, 5]); - - let v2: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c2 = v2.array_chunks_mut::<2>(); - assert_eq!(c2.last().unwrap(), &[2, 3]); -} - -#[test] -fn test_array_chunks_mut_remainder() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let c = v.array_chunks_mut::<2>(); - assert_eq!(c.into_remainder(), &[4]); -} - -#[test] -fn test_array_chunks_mut_zip() { - let v1: &mut [i32] = &mut [0, 1, 2, 3, 4]; - let v2: &[i32] = &[6, 7, 8, 9, 10]; - - for (a, b) in v1.array_chunks_mut::<2>().zip(v2.array_chunks::<2>()) { - let sum = b.iter().sum::(); - for v in a { - *v += sum; - } - } - assert_eq!(v1, [13, 14, 19, 20, 4]); -} - #[test] fn test_array_windows_infer() { let v: &[i32] = &[0, 1, 0, 1]; diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 323742a75b05..433e013b40fd 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -324,7 +324,6 @@ // // Library features (core): // tidy-alphabetical-start -#![feature(array_chunks)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(char_internals)] diff --git a/library/std/src/sys/random/sgx.rs b/library/std/src/sys/random/sgx.rs index c3647a8df220..462b19003fad 100644 --- a/library/std/src/sys/random/sgx.rs +++ b/library/std/src/sys/random/sgx.rs @@ -46,22 +46,22 @@ fn rdrand16() -> u16 { } pub fn fill_bytes(bytes: &mut [u8]) { - let mut chunks = bytes.array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = bytes.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand64().to_ne_bytes(); } - let mut chunks = chunks.into_remainder().array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = remainder.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand32().to_ne_bytes(); } - let mut chunks = chunks.into_remainder().array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, remainder) = remainder.as_chunks_mut(); + for chunk in chunks { *chunk = rdrand16().to_ne_bytes(); } - if let [byte] = chunks.into_remainder() { + if let [byte] = remainder { *byte = rdrand16() as u8; } } diff --git a/library/std/src/sys/random/uefi.rs b/library/std/src/sys/random/uefi.rs index 5f001f0f532a..4a71d32fffeb 100644 --- a/library/std/src/sys/random/uefi.rs +++ b/library/std/src/sys/random/uefi.rs @@ -138,12 +138,11 @@ mod rdrand { } unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> { - let mut chunks = dest.array_chunks_mut(); - for chunk in &mut chunks { + let (chunks, tail) = dest.as_chunks_mut(); + for chunk in chunks { *chunk = unsafe { rdrand() }?.to_ne_bytes(); } - let tail = chunks.into_remainder(); let n = tail.len(); if n > 0 { let src = unsafe { rdrand() }?.to_ne_bytes(); From d344aa849006a9a36928b4bb5f36fea7b6b5476a Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 28 Jul 2025 06:30:59 +0000 Subject: [PATCH 114/201] Prepare for merging from rust-lang/rust This updates the rust-version file to 733dab558992d902d6d17576de1da768094e2cf3. --- src/tools/rust-analyzer/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index c2b1c151b834..2178caf63968 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e05ab47e6c418fb2b9faa2eae9a7e70c65c98eaa +733dab558992d902d6d17576de1da768094e2cf3 From fbd553e8e579887210e7a8e71f41f631d3054e67 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Jul 2025 08:38:15 +0200 Subject: [PATCH 115/201] prepare for sync --- 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 634ce1fa0628..2178caf63968 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -b56aaec52bc0fa35591a872fb4aac81f606e265c +733dab558992d902d6d17576de1da768094e2cf3 From dea3e131d53eccd46703a304d942778b5c6edef6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Jul 2025 08:42:04 +0200 Subject: [PATCH 116/201] revert accidental change --- src/tools/miri/src/diagnostics.rs | 2 +- .../fail/function_pointers/abi_mismatch_return_type.stderr | 2 ++ src/tools/miri/tests/fail/shims/return_type_mismatch.stderr | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 8fdf8d643eae..9ecbd31c5b9f 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -382,7 +382,7 @@ pub fn report_error<'tcx>( helps.push(note_span!(span, "{:?} was deallocated here:", alloc_id)); } } - AbiMismatchArgument { .. } => { + AbiMismatchArgument { .. } | AbiMismatchReturn { .. } => { helps.push(note!("this means these two types are not *guaranteed* to be ABI-compatible across all targets")); helps.push(note!("if you think this code should be accepted anyway, please report an issue with Miri")); } diff --git a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr index 07e6561b53e7..28c676ad4823 100644 --- a/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr +++ b/src/tools/miri/tests/fail/function_pointers/abi_mismatch_return_type.stderr @@ -6,6 +6,8 @@ LL | g() | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/function_pointers/abi_mismatch_return_type.rs:LL:CC diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr index 080ee16a2eb5..18ff3067fa03 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.stderr @@ -6,6 +6,8 @@ LL | close(fd); | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets + = help: if you think this code should be accepted anyway, please report an issue with Miri = note: BACKTRACE: = note: inside `main` at tests/fail/shims/return_type_mismatch.rs:LL:CC From ecb046a63e94301463b8a5291d7ef9776c06aadc Mon Sep 17 00:00:00 2001 From: Patrick-6 Date: Mon, 28 Jul 2025 10:50:02 +0200 Subject: [PATCH 117/201] Reduce required cc crate version. --- src/tools/miri/Cargo.lock | 4 ++-- src/tools/miri/genmc-sys/Cargo.toml | 2 +- src/tools/miri/genmc-sys/build.rs | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index ece51f2ba74a..b46f0f834209 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml index 95aef75afc4f..737ab9073bfb 100644 --- a/src/tools/miri/genmc-sys/Cargo.toml +++ b/src/tools/miri/genmc-sys/Cargo.toml @@ -11,7 +11,7 @@ edition = "2024" cxx = { version = "1.0.160", features = ["c++20"] } [build-dependencies] -cc = "1.2.30" +cc = "1.2.16" cmake = "0.1.54" git2 = { version = "0.20.2", default-features = false, features = ["https"] } cxx-build = { version = "1.0.160", features = ["parallel"] } diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs index f20e0e8d525f..479a3bd7186b 100644 --- a/src/tools/miri/genmc-sys/build.rs +++ b/src/tools/miri/genmc-sys/build.rs @@ -215,8 +215,10 @@ fn compile_cpp_dependencies(genmc_path: &Path) { if enable_genmc_debug { bridge.define("ENABLE_GENMC_DEBUG", None); } + for definition in definitions { + bridge.flag(definition); + } bridge - .flags(definitions) .opt_level(2) .debug(true) // Same settings that GenMC uses (default for cmake `RelWithDebInfo`) .warnings(false) // NOTE: enabling this produces a lot of warnings. From f29f073f43f7cd2732a040bf2369d02c84239dc0 Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Sun, 13 Jul 2025 12:38:21 +0200 Subject: [PATCH 118/201] add support for ./x check src/tools/linkchecker --- src/bootstrap/src/core/build_steps/check.rs | 6 ++++++ src/bootstrap/src/core/builder/mod.rs | 1 + 2 files changed, 7 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index cfe090b22dc3..b4232409ba83 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -556,3 +556,9 @@ tool_check_step!(Compiletest { allow_features: COMPILETEST_ALLOW_FEATURES, default: false, }); + +tool_check_step!(Linkchecker { + path: "src/tools/linkchecker", + mode: |_builder| Mode::ToolBootstrap, + default: false +}); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 923c3a9a9350..020622d1c121 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1033,6 +1033,7 @@ impl<'a> Builder<'a> { check::Compiletest, check::FeaturesStatusDump, check::CoverageDump, + check::Linkchecker, // This has special staging logic, it may run on stage 1 while others run on stage 0. // It takes quite some time to build stage 1, so put this at the end. // From c745614e03bd820a0b6fd4f0962f21c66464c1fb Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Sun, 13 Jul 2025 12:40:32 +0200 Subject: [PATCH 119/201] bump linkchecker to edition 2024 --- src/tools/linkchecker/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml index 7123d43eb564..fb5bff3fe63f 100644 --- a/src/tools/linkchecker/Cargo.toml +++ b/src/tools/linkchecker/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "linkchecker" version = "0.1.0" -edition = "2021" +edition = "2024" [[bin]] name = "linkchecker" From 984926eb3a0b96a6fed8d721b93ccd8efeb8510e Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 1 Jul 2025 10:57:52 +0200 Subject: [PATCH 120/201] add an argument parser to linkchecker --- src/tools/linkchecker/main.rs | 51 +++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 84cba3f8c447..4b45692e8490 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -18,11 +18,11 @@ use std::cell::{Cell, RefCell}; use std::collections::{HashMap, HashSet}; +use std::fs; use std::io::ErrorKind; use std::path::{Component, Path, PathBuf}; use std::rc::Rc; use std::time::Instant; -use std::{env, fs}; use html5ever::tendril::ByteTendril; use html5ever::tokenizer::{ @@ -110,10 +110,20 @@ macro_rules! t { }; } +struct Cli { + docs: PathBuf, +} + fn main() { - let docs = env::args_os().nth(1).expect("doc path should be first argument"); - let docs = env::current_dir().unwrap().join(docs); - let mut checker = Checker { root: docs.clone(), cache: HashMap::new() }; + let cli = match parse_cli() { + Ok(cli) => cli, + Err(err) => { + eprintln!("error: {err}"); + usage_and_exit(1); + } + }; + + let mut checker = Checker { root: cli.docs.clone(), cache: HashMap::new() }; let mut report = Report { errors: 0, start: Instant::now(), @@ -125,7 +135,7 @@ fn main() { intra_doc_exceptions: 0, has_broken_urls: false, }; - checker.walk(&docs, &mut report); + checker.walk(&cli.docs, &mut report); report.report(); if report.errors != 0 { println!("found some broken links"); @@ -133,6 +143,37 @@ fn main() { } } +fn parse_cli() -> Result { + fn to_canonical_path(arg: &str) -> Result { + PathBuf::from(arg).canonicalize().map_err(|e| format!("could not canonicalize {arg}: {e}")) + } + + let mut verbatim = false; + let mut docs = None; + + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + if !verbatim && arg == "--" { + verbatim = true; + } else if !verbatim && (arg == "-h" || arg == "--help") { + usage_and_exit(0) + } else if !verbatim && arg.starts_with('-') { + return Err(format!("unknown flag: {arg}")); + } else if docs.is_none() { + docs = Some(arg); + } else { + return Err("too many positional arguments".into()); + } + } + + Ok(Cli { docs: to_canonical_path(&docs.ok_or("missing first positional argument")?)? }) +} + +fn usage_and_exit(code: i32) -> ! { + eprintln!("usage: linkchecker "); + std::process::exit(code) +} + struct Checker { root: PathBuf, cache: Cache, From 2b11851452364e07a3b0456a7a24424d944297fa Mon Sep 17 00:00:00 2001 From: Boxy Date: Mon, 28 Jul 2025 09:59:57 +0100 Subject: [PATCH 121/201] Raw Pointers are Constant PatKinds too --- compiler/rustc_middle/src/thir.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 730c1147684b..3dd6d2c89286 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -838,6 +838,8 @@ pub enum PatKind<'tcx> { /// * integer, bool, char or float (represented as a valtree), which will be handled by /// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are /// much simpler. + /// * raw pointers derived from integers, other raw pointers will have already resulted in an + // error. /// * `String`, if `string_deref_patterns` is enabled. Constant { value: mir::Const<'tcx>, From 6693b3908f36374cd998e43445760f2b1552973d Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Tue, 1 Jul 2025 12:16:35 +0200 Subject: [PATCH 122/201] add --link-targets-dir flag to linkchecker --- src/tools/linkchecker/main.rs | 77 +++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 4b45692e8490..1dc45728c90c 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -17,9 +17,10 @@ //! should catch the majority of "broken link" cases. use std::cell::{Cell, RefCell}; +use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::fs; -use std::io::ErrorKind; +use std::iter::once; use std::path::{Component, Path, PathBuf}; use std::rc::Rc; use std::time::Instant; @@ -112,6 +113,7 @@ macro_rules! t { struct Cli { docs: PathBuf, + link_targets_dirs: Vec, } fn main() { @@ -123,7 +125,11 @@ fn main() { } }; - let mut checker = Checker { root: cli.docs.clone(), cache: HashMap::new() }; + let mut checker = Checker { + root: cli.docs.clone(), + link_targets_dirs: cli.link_targets_dirs, + cache: HashMap::new(), + }; let mut report = Report { errors: 0, start: Instant::now(), @@ -144,12 +150,13 @@ fn main() { } fn parse_cli() -> Result { - fn to_canonical_path(arg: &str) -> Result { - PathBuf::from(arg).canonicalize().map_err(|e| format!("could not canonicalize {arg}: {e}")) + fn to_absolute_path(arg: &str) -> Result { + std::path::absolute(arg).map_err(|e| format!("could not convert to absolute {arg}: {e}")) } let mut verbatim = false; let mut docs = None; + let mut link_targets_dirs = Vec::new(); let mut args = std::env::args().skip(1); while let Some(arg) = args.next() { @@ -157,6 +164,12 @@ fn parse_cli() -> Result { verbatim = true; } else if !verbatim && (arg == "-h" || arg == "--help") { usage_and_exit(0) + } else if !verbatim && arg == "--link-targets-dir" { + link_targets_dirs.push(to_absolute_path( + &args.next().ok_or("missing value for --link-targets-dir")?, + )?); + } else if !verbatim && let Some(value) = arg.strip_prefix("--link-targets-dir=") { + link_targets_dirs.push(to_absolute_path(value)?); } else if !verbatim && arg.starts_with('-') { return Err(format!("unknown flag: {arg}")); } else if docs.is_none() { @@ -166,16 +179,20 @@ fn parse_cli() -> Result { } } - Ok(Cli { docs: to_canonical_path(&docs.ok_or("missing first positional argument")?)? }) + Ok(Cli { + docs: to_absolute_path(&docs.ok_or("missing first positional argument")?)?, + link_targets_dirs, + }) } fn usage_and_exit(code: i32) -> ! { - eprintln!("usage: linkchecker "); + eprintln!("usage: linkchecker PATH [--link-targets-dir=PATH ...]"); std::process::exit(code) } struct Checker { root: PathBuf, + link_targets_dirs: Vec, cache: Cache, } @@ -461,37 +478,34 @@ impl Checker { /// Load a file from disk, or from the cache if available. fn load_file(&mut self, file: &Path, report: &mut Report) -> (String, &FileEntry) { - // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- - #[cfg(windows)] - const ERROR_INVALID_NAME: i32 = 123; - let pretty_path = file.strip_prefix(&self.root).unwrap_or(file).to_str().unwrap().to_string(); - let entry = - self.cache.entry(pretty_path.clone()).or_insert_with(|| match fs::metadata(file) { + for base in once(&self.root).chain(self.link_targets_dirs.iter()) { + let entry = self.cache.entry(pretty_path.clone()); + if let Entry::Occupied(e) = &entry + && !matches!(e.get(), FileEntry::Missing) + { + break; + } + + let file = base.join(&pretty_path); + entry.insert_entry(match fs::metadata(&file) { Ok(metadata) if metadata.is_dir() => FileEntry::Dir, Ok(_) => { if file.extension().and_then(|s| s.to_str()) != Some("html") { FileEntry::OtherFile } else { report.html_files += 1; - load_html_file(file, report) + load_html_file(&file, report) } } - Err(e) if e.kind() == ErrorKind::NotFound => FileEntry::Missing, - Err(e) => { - // If a broken intra-doc link contains `::`, on windows, it will cause `ERROR_INVALID_NAME` rather than `NotFound`. - // Explicitly check for that so that the broken link can be allowed in `LINKCHECK_EXCEPTIONS`. - #[cfg(windows)] - if e.raw_os_error() == Some(ERROR_INVALID_NAME) - && file.as_os_str().to_str().map_or(false, |s| s.contains("::")) - { - return FileEntry::Missing; - } - panic!("unexpected read error for {}: {}", file.display(), e); - } + Err(e) if is_not_found_error(&file, &e) => FileEntry::Missing, + Err(e) => panic!("unexpected read error for {}: {}", file.display(), e), }); + } + + let entry = self.cache.get(&pretty_path).unwrap(); (pretty_path, entry) } } @@ -670,3 +684,16 @@ fn parse_ids(ids: &mut HashSet, file: &str, source: &str, report: &mut R ids.insert(encoded); } } + +fn is_not_found_error(path: &Path, error: &std::io::Error) -> bool { + // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- + const WINDOWS_ERROR_INVALID_NAME: i32 = 123; + + error.kind() == std::io::ErrorKind::NotFound + // If a broken intra-doc link contains `::`, on windows, it will cause `ERROR_INVALID_NAME` + // rather than `NotFound`. Explicitly check for that so that the broken link can be allowed + // in `LINKCHECK_EXCEPTIONS`. + || (cfg!(windows) + && error.raw_os_error() == Some(WINDOWS_ERROR_INVALID_NAME) + && path.as_os_str().to_str().map_or(false, |s| s.contains("::"))) +} From c340233769438df7299624b2d0de791995245518 Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Mon, 28 Jul 2025 15:05:36 +0530 Subject: [PATCH 123/201] fixed typo chunks->as_chunks --- library/core/src/slice/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 6fe5affc48be..536d6e1e7c30 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1232,7 +1232,7 @@ impl [T] { /// /// [`chunks`]: slice::chunks /// [`rchunks_exact`]: slice::rchunks_exact - /// [`as_chunks`]: slice::chunks + /// [`as_chunks`]: slice::as_chunks #[stable(feature = "chunks_exact", since = "1.31.0")] #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] #[inline] From 68f08c5dd9f5bee706a221df13660cc9b3a71c93 Mon Sep 17 00:00:00 2001 From: Yosh Date: Mon, 21 Jul 2025 03:04:17 +0200 Subject: [PATCH 124/201] Add `core::mem::DropGuard` Fix CI for drop_guard fix CI fix all tidy lints fix tidy link add first batch of feedback from review Add second batch of feedback from review add third batch of feedback from review fix failing test Update library/core/src/mem/drop_guard.rs Co-authored-by: Ruby Lazuli fix doctests Implement changes from T-Libs-API review And start tracking based on the tracking issue. fix tidy lint --- library/core/src/mem/drop_guard.rs | 155 +++++++++++++++++++++++++++++ library/core/src/mem/mod.rs | 4 + library/coretests/tests/lib.rs | 1 + library/coretests/tests/mem.rs | 46 +++++++++ library/std/src/lib.rs | 1 + 5 files changed, 207 insertions(+) create mode 100644 library/core/src/mem/drop_guard.rs diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs new file mode 100644 index 000000000000..47ccb69acc80 --- /dev/null +++ b/library/core/src/mem/drop_guard.rs @@ -0,0 +1,155 @@ +use crate::fmt::{self, Debug}; +use crate::mem::ManuallyDrop; +use crate::ops::{Deref, DerefMut}; + +/// Wrap a value and run a closure when dropped. +/// +/// This is useful for quickly creating desructors inline. +/// +/// # Examples +/// +/// ```rust +/// # #![allow(unused)] +/// #![feature(drop_guard)] +/// +/// use std::mem::DropGuard; +/// +/// { +/// // Create a new guard around a string that will +/// // print its value when dropped. +/// let s = String::from("Chashu likes tuna"); +/// let mut s = DropGuard::new(s, |s| println!("{s}")); +/// +/// // Modify the string contained in the guard. +/// s.push_str("!!!"); +/// +/// // The guard will be dropped here, printing: +/// // "Chashu likes tuna!!!" +/// } +/// ``` +#[unstable(feature = "drop_guard", issue = "144426")] +#[doc(alias = "ScopeGuard")] +#[doc(alias = "defer")] +pub struct DropGuard +where + F: FnOnce(T), +{ + inner: ManuallyDrop, + f: ManuallyDrop, +} + +impl DropGuard +where + F: FnOnce(T), +{ + /// Create a new instance of `DropGuard`. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(drop_guard)] + /// + /// use std::mem::DropGuard; + /// + /// let value = String::from("Chashu likes tuna"); + /// let guard = DropGuard::new(value, |s| println!("{s}")); + /// ``` + #[unstable(feature = "drop_guard", issue = "144426")] + #[must_use] + pub const fn new(inner: T, f: F) -> Self { + Self { inner: ManuallyDrop::new(inner), f: ManuallyDrop::new(f) } + } + + /// Consumes the `DropGuard`, returning the wrapped value. + /// + /// This will not execute the closure. This is implemented as an associated + /// function to prevent any potential conflicts with any other methods called + /// `into_inner` from the `Deref` and `DerefMut` impls. + /// + /// It is typically preferred to call this function instead of `mem::forget` + /// because it will return the stored value and drop variables captured + /// by the closure instead of leaking their owned resources. + /// + /// # Example + /// + /// ```rust + /// # #![allow(unused)] + /// #![feature(drop_guard)] + /// + /// use std::mem::DropGuard; + /// + /// let value = String::from("Nori likes chicken"); + /// let guard = DropGuard::new(value, |s| println!("{s}")); + /// assert_eq!(DropGuard::into_inner(guard), "Nori likes chicken"); + /// ``` + #[unstable(feature = "drop_guard", issue = "144426")] + #[inline] + pub fn into_inner(guard: Self) -> T { + // First we ensure that dropping the guard will not trigger + // its destructor + let mut guard = ManuallyDrop::new(guard); + + // Next we manually read the stored value from the guard. + // + // SAFETY: this is safe because we've taken ownership of the guard. + let value = unsafe { ManuallyDrop::take(&mut guard.inner) }; + + // Finally we drop the stored closure. We do this *after* having read + // the value, so that even if the closure's `drop` function panics, + // unwinding still tries to drop the value. + // + // SAFETY: this is safe because we've taken ownership of the guard. + unsafe { ManuallyDrop::drop(&mut guard.f) }; + value + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Deref for DropGuard +where + F: FnOnce(T), +{ + type Target = T; + + fn deref(&self) -> &T { + &*self.inner + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl DerefMut for DropGuard +where + F: FnOnce(T), +{ + fn deref_mut(&mut self) -> &mut T { + &mut *self.inner + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Drop for DropGuard +where + F: FnOnce(T), +{ + fn drop(&mut self) { + // SAFETY: `DropGuard` is in the process of being dropped. + let inner = unsafe { ManuallyDrop::take(&mut self.inner) }; + + // SAFETY: `DropGuard` is in the process of being dropped. + let f = unsafe { ManuallyDrop::take(&mut self.f) }; + + f(inner); + } +} + +#[unstable(feature = "drop_guard", issue = "144426")] +impl Debug for DropGuard +where + T: Debug, + F: FnOnce(T), +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index 2198d098c4be..fcb9f25ede2f 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -21,6 +21,10 @@ mod transmutability; #[unstable(feature = "transmutability", issue = "99571")] pub use transmutability::{Assume, TransmuteFrom}; +mod drop_guard; +#[unstable(feature = "drop_guard", issue = "144426")] +pub use drop_guard::DropGuard; + // This one has to be a re-export (rather than wrapping the underlying intrinsic) so that we can do // the special magic "types have equal size" check at the call site. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 4cfac9ecc2ab..c8f516f0a955 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -30,6 +30,7 @@ #![feature(core_private_diy_float)] #![feature(cstr_display)] #![feature(dec2flt)] +#![feature(drop_guard)] #![feature(duration_constants)] #![feature(duration_constructors)] #![feature(duration_constructors_lite)] diff --git a/library/coretests/tests/mem.rs b/library/coretests/tests/mem.rs index 9c15be4a8c4b..e896c61ef488 100644 --- a/library/coretests/tests/mem.rs +++ b/library/coretests/tests/mem.rs @@ -1,5 +1,6 @@ use core::mem::*; use core::{array, ptr}; +use std::cell::Cell; #[cfg(panic = "unwind")] use std::rc::Rc; @@ -795,3 +796,48 @@ fn const_maybe_uninit_zeroed() { assert_eq!(unsafe { (*UNINIT.0.cast::<[[u8; SIZE]; 1]>())[0] }, [0u8; SIZE]); } + +#[test] +fn drop_guards_only_dropped_by_closure_when_run() { + let value_drops = Cell::new(0); + let value = DropGuard::new((), |()| value_drops.set(1 + value_drops.get())); + let closure_drops = Cell::new(0); + let guard = DropGuard::new(value, |_| closure_drops.set(1 + closure_drops.get())); + assert_eq!(value_drops.get(), 0); + assert_eq!(closure_drops.get(), 0); + drop(guard); + assert_eq!(value_drops.get(), 1); + assert_eq!(closure_drops.get(), 1); +} + +#[test] +fn drop_guard_into_inner() { + let dropped = Cell::new(false); + let value = DropGuard::new(42, |_| dropped.set(true)); + let guard = DropGuard::new(value, |_| dropped.set(true)); + let inner = DropGuard::into_inner(guard); + assert_eq!(dropped.get(), false); + assert_eq!(*inner, 42); +} + +#[test] +#[cfg(panic = "unwind")] +fn drop_guard_always_drops_value_if_closure_drop_unwinds() { + // Create a value with a destructor, which we will validate ran successfully. + let mut value_was_dropped = false; + let value_with_tracked_destruction = DropGuard::new((), |_| value_was_dropped = true); + + // Create a closure that will begin unwinding when dropped. + let drop_bomb = DropGuard::new((), |_| panic!()); + let closure_that_panics_on_drop = move |_| { + let _drop_bomb = drop_bomb; + }; + + // This will run the closure, which will panic when dropped. This should + // run the destructor of the value we passed, which we validate. + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let guard = DropGuard::new(value_with_tracked_destruction, closure_that_panics_on_drop); + DropGuard::into_inner(guard); + })); + assert!(value_was_dropped); +} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 323742a75b05..1b1e85cfc2fe 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -331,6 +331,7 @@ #![feature(clone_to_uninit)] #![feature(core_intrinsics)] #![feature(core_io_borrowed_buf)] +#![feature(drop_guard)] #![feature(duration_constants)] #![feature(error_generic_member_access)] #![feature(error_iter)] From 45b0c8d03e627d55f814a4cd03b90c5068664ac8 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 24 Jul 2025 08:07:56 +0000 Subject: [PATCH 125/201] Add regression test --- tests/ui/enum-discriminant/wrapping_niche.rs | 24 ++ .../enum-discriminant/wrapping_niche.stderr | 229 ++++++++++++++++++ 2 files changed, 253 insertions(+) create mode 100644 tests/ui/enum-discriminant/wrapping_niche.rs create mode 100644 tests/ui/enum-discriminant/wrapping_niche.stderr diff --git a/tests/ui/enum-discriminant/wrapping_niche.rs b/tests/ui/enum-discriminant/wrapping_niche.rs new file mode 100644 index 000000000000..8097414be687 --- /dev/null +++ b/tests/ui/enum-discriminant/wrapping_niche.rs @@ -0,0 +1,24 @@ +//! Test that we produce the same niche range no +//! matter of signendess if the discriminants are the same. + +#![feature(rustc_attrs)] + +#[repr(u16)] +#[rustc_layout(debug)] +enum UnsignedAroundZero { + //~^ ERROR: layout_of + A = 65535, + B = 0, + C = 1, +} + +#[repr(i16)] +#[rustc_layout(debug)] +enum SignedAroundZero { + //~^ ERROR: layout_of + A = -1, + B = 0, + C = 1, +} + +fn main() {} diff --git a/tests/ui/enum-discriminant/wrapping_niche.stderr b/tests/ui/enum-discriminant/wrapping_niche.stderr new file mode 100644 index 000000000000..76cafa43e905 --- /dev/null +++ b/tests/ui/enum-discriminant/wrapping_niche.stderr @@ -0,0 +1,229 @@ +error: layout_of(UnsignedAroundZero) = Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Int( + I16, + false, + ), + valid_range: 0..=65535, + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: None, + uninhabited: false, + variants: Multiple { + tag: Initialized { + value: Int( + I16, + false, + ), + valid_range: 0..=65535, + }, + tag_encoding: Direct, + tag_field: 0, + variants: [ + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 9885373149222004003, + }, + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 1, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 9885373149222004003, + }, + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 2, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 9885373149222004003, + }, + ], + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 2648004449468912780, + } + --> $DIR/wrapping_niche.rs:8:1 + | +LL | enum UnsignedAroundZero { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: layout_of(SignedAroundZero) = Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Scalar( + Initialized { + value: Int( + I16, + true, + ), + valid_range: (..=1) | (65535..), + }, + ), + fields: Arbitrary { + offsets: [ + Size(0 bytes), + ], + memory_index: [ + 0, + ], + }, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I16, + true, + ), + valid_range: (..=1) | (65535..), + }, + ), + uninhabited: false, + variants: Multiple { + tag: Initialized { + value: Int( + I16, + true, + ), + valid_range: (..=1) | (65535..), + }, + tag_encoding: Direct, + tag_field: 0, + variants: [ + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 0, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 2684536712112553499, + }, + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 1, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 2684536712112553499, + }, + Layout { + size: Size(2 bytes), + align: AbiAlign { + abi: Align(2 bytes), + }, + backend_repr: Memory { + sized: true, + }, + fields: Arbitrary { + offsets: [], + memory_index: [], + }, + largest_niche: None, + uninhabited: false, + variants: Single { + index: 2, + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 2684536712112553499, + }, + ], + }, + max_repr_align: None, + unadjusted_abi_align: Align(2 bytes), + randomization_seed: 10738146848450213996, + } + --> $DIR/wrapping_niche.rs:17:1 + | +LL | enum SignedAroundZero { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From d51db69e09e00b2283c18f0cdf5415992a396592 Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Mon, 28 Jul 2025 11:28:24 +0100 Subject: [PATCH 126/201] Don't show '$saved_file' literally in IDE status updates We've had a few users get confused when VS Code shows `my_custom_check --args $saved_file`, as it looks like substitution didn't occur. Instead, show `my_custom_check --args ...` in the display output. This is also shorter, and the VS Code status bar generally works best with short text. --- .../crates/rust-analyzer/src/flycheck.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index bec57a4beded..512ce0b9de35 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -111,7 +111,18 @@ impl fmt::Display for FlycheckConfig { match self { FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"), FlycheckConfig::CustomCommand { command, args, .. } => { - write!(f, "{command} {}", args.join(" ")) + // Don't show `my_custom_check --foo $saved_file` literally to the user, as it + // looks like we've forgotten to substitute $saved_file. + // + // Instead, show `my_custom_check --foo ...`. The + // actual path is often too long to be worth showing + // in the IDE (e.g. in the VS Code status bar). + let display_args = args + .iter() + .map(|arg| if arg == SAVED_FILE_PLACEHOLDER { "..." } else { arg }) + .collect::>(); + + write!(f, "{command} {}", display_args.join(" ")) } } } From e008fe0c1865e6f307c1e4e6260612837051cb8b Mon Sep 17 00:00:00 2001 From: Kornel Date: Sat, 19 Jul 2025 22:49:06 +0100 Subject: [PATCH 127/201] Avoid redundant lookup in CrateLoader::existing_match --- compiler/rustc_metadata/src/creader.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 438eff330548..6d3dab267ac3 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -511,7 +511,7 @@ impl CStore { // We're also sure to compare *paths*, not actual byte slices. The // `source` stores paths which are normalized which may be different // from the strings on the command line. - let source = self.get_crate_data(cnum).cdata.source(); + let source = data.source(); if let Some(entry) = externs.get(name.as_str()) { // Only use `--extern crate_name=path` here, not `--extern crate_name`. if let Some(mut files) = entry.files() { From 671e083391df7d7ea3b70c8030be578d17b5743b Mon Sep 17 00:00:00 2001 From: Kornel Date: Thu, 3 Jul 2025 13:41:54 +0100 Subject: [PATCH 128/201] Clarify update_extern_crate --- compiler/rustc_metadata/src/rmeta/decoder.rs | 8 +++++-- .../src/rmeta/decoder/cstore_impl.rs | 24 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index e6aedc61338b..00c97a2f738e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1937,9 +1937,13 @@ impl CrateMetadata { self.root.decode_target_modifiers(&self.blob).collect() } - pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool { + /// Keep `new_extern_crate` if it looks better in diagnostics + pub(crate) fn update_extern_crate_diagnostics( + &mut self, + new_extern_crate: ExternCrate, + ) -> bool { let update = - Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank); + self.extern_crate.as_ref().is_none_or(|old| old.rank() < new_extern_crate.rank()); if update { self.extern_crate = Some(new_extern_crate); } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 57a672c45f7b..989741c004a1 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -627,14 +627,32 @@ impl CStore { } } - pub(crate) fn update_extern_crate(&mut self, cnum: CrateNum, extern_crate: ExternCrate) { + /// Track how an extern crate has been loaded. Called after resolving an import in the local crate. + pub(crate) fn update_extern_crate( + &mut self, + cnum: CrateNum, + extern_crate: ExternCrate, + ) { + debug_assert_eq!( + extern_crate.dependency_of, LOCAL_CRATE, + "this function should not be called on transitive dependencies" + ); + self.update_transitive_extern_crate_diagnostics(cnum, extern_crate); + } + + /// `CrateMetadata` uses `ExternCrate` only for diagnostics + fn update_transitive_extern_crate_diagnostics( + &mut self, + cnum: CrateNum, + extern_crate: ExternCrate, + ) { let cmeta = self.get_crate_data_mut(cnum); - if cmeta.update_extern_crate(extern_crate) { + if cmeta.update_extern_crate_diagnostics(extern_crate) { // Propagate the extern crate info to dependencies if it was updated. let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate }; let dependencies = mem::take(&mut cmeta.dependencies); for &dep_cnum in &dependencies { - self.update_extern_crate(dep_cnum, extern_crate); + self.update_transitive_extern_crate_diagnostics(dep_cnum, extern_crate); } self.get_crate_data_mut(cnum).dependencies = dependencies; } From 8a0f97604777cbdb0ea027e6ca741cc0b13e64c9 Mon Sep 17 00:00:00 2001 From: Kornel Date: Tue, 15 Jul 2025 10:48:17 +0100 Subject: [PATCH 129/201] Save names of used extern crates Tracks association between `self.sess.opts.externs` (aliases in `--extern alias=rlib`) and resolved `CrateNum` Intended to allow Rustdoc match the aliases in `--extern-html-root-url` Force-injected extern crates aren't included, since they're meant for the linker only --- compiler/rustc_metadata/src/creader.rs | 15 +++++++++++++++ .../src/rmeta/decoder/cstore_impl.rs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 6d3dab267ac3..127a773e5e80 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -12,6 +12,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::owned_slice::OwnedSlice; use rustc_data_structures::svh::Svh; use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard}; +use rustc_data_structures::unord::UnordMap; use rustc_expand::base::SyntaxExtension; use rustc_fs_util::try_canonicalize; use rustc_hir as hir; @@ -69,6 +70,9 @@ pub struct CStore { /// This crate has a `#[alloc_error_handler]` item. has_alloc_error_handler: bool, + /// Names that were used to load the crates via `extern crate` or paths. + resolved_externs: UnordMap, + /// Unused externs of the crate unused_externs: Vec, @@ -249,6 +253,14 @@ impl CStore { self.metas[cnum] = Some(Box::new(data)); } + /// Save the name used to resolve the extern crate in the local crate + /// + /// The name isn't always the crate's own name, because `sess.opts.externs` can assign it another name. + /// It's also not always the same as the `DefId`'s symbol due to renames `extern crate resolved_name as defid_name`. + pub(crate) fn set_resolved_extern_crate_name(&mut self, name: Symbol, extern_crate: CrateNum) { + self.resolved_externs.insert(name, extern_crate); + } + pub(crate) fn iter_crate_data(&self) -> impl Iterator { self.metas .iter_enumerated() @@ -475,6 +487,7 @@ impl CStore { alloc_error_handler_kind: None, has_global_allocator: false, has_alloc_error_handler: false, + resolved_externs: UnordMap::default(), unused_externs: Vec::new(), used_extern_options: Default::default(), } @@ -1308,6 +1321,7 @@ impl CStore { let path_len = definitions.def_path(def_id).data.len(); self.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Extern(def_id.to_def_id()), span: item.span, @@ -1332,6 +1346,7 @@ impl CStore { self.update_extern_crate( cnum, + name, ExternCrate { src: ExternCrateSource::Path, span, diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 989741c004a1..9415e420eed5 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -628,15 +628,20 @@ impl CStore { } /// Track how an extern crate has been loaded. Called after resolving an import in the local crate. + /// + /// * the `name` is for [`Self::set_resolved_extern_crate_name`] saving `--extern name=` + /// * `extern_crate` is for diagnostics pub(crate) fn update_extern_crate( &mut self, cnum: CrateNum, + name: Symbol, extern_crate: ExternCrate, ) { debug_assert_eq!( extern_crate.dependency_of, LOCAL_CRATE, "this function should not be called on transitive dependencies" ); + self.set_resolved_extern_crate_name(name, cnum); self.update_transitive_extern_crate_diagnostics(cnum, extern_crate); } From 0813cc9dcf755bb04ef396bfa94218b56bb08c4a Mon Sep 17 00:00:00 2001 From: Kornel Date: Sat, 5 Jul 2025 02:00:57 +0100 Subject: [PATCH 130/201] Test renamed crates in rustdoc --- tests/rustdoc/extern/extern-html-alias.rs | 9 +++++++++ tests/rustdoc/extern/extern-html-fallback.rs | 14 ++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 tests/rustdoc/extern/extern-html-alias.rs create mode 100644 tests/rustdoc/extern/extern-html-fallback.rs diff --git a/tests/rustdoc/extern/extern-html-alias.rs b/tests/rustdoc/extern/extern-html-alias.rs new file mode 100644 index 000000000000..3ff782d3963c --- /dev/null +++ b/tests/rustdoc/extern/extern-html-alias.rs @@ -0,0 +1,9 @@ +//@ compile-flags:-Z unstable-options --extern-html-root-url externs_name=https://renamed.example.com --extern-html-root-url empty=https://bad.invalid +//@ aux-crate:externs_name=empty.rs +//@ edition: 2018 + +extern crate externs_name as renamed; + +//@ has extern_html_alias/index.html +//@ has - '//a/@href' 'https://renamed.example.com/empty/index.html' +pub use renamed as yet_different_name; diff --git a/tests/rustdoc/extern/extern-html-fallback.rs b/tests/rustdoc/extern/extern-html-fallback.rs new file mode 100644 index 000000000000..ddac9bf713c6 --- /dev/null +++ b/tests/rustdoc/extern/extern-html-fallback.rs @@ -0,0 +1,14 @@ +//@ compile-flags:-Z unstable-options --extern-html-root-url yet_another_name=https://bad.invalid --extern-html-root-url renamed_privately=https://bad.invalid --extern-html-root-url renamed_locally=https://bad.invalid --extern-html-root-url empty=https://localhost +//@ aux-crate:externs_name=empty.rs +//@ edition: 2018 + +mod m { + pub extern crate externs_name as renamed_privately; +} + +// renaming within the crate's source code is not supposed to affect CLI flags +extern crate externs_name as renamed_locally; + +//@ has extern_html_fallback/index.html +//@ has - '//a/@href' 'https://localhost/empty/index.html' +pub use crate::renamed_locally as yet_another_name; From 276c4238a7ee0fa472c951f357642778d60a3bdb Mon Sep 17 00:00:00 2001 From: Kornel Date: Thu, 3 Jul 2025 15:13:19 +0100 Subject: [PATCH 131/201] Support multiple crate versions in --extern-html-root-url --- compiler/rustc_metadata/src/creader.rs | 8 +++++++ src/doc/rustdoc/src/unstable-features.md | 6 +++++ src/librustdoc/formats/cache.rs | 30 ++++++++++++++++++------ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 127a773e5e80..6bfb3769f247 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -261,6 +261,14 @@ impl CStore { self.resolved_externs.insert(name, extern_crate); } + /// Crate resolved and loaded via the given extern name + /// (corresponds to names in `sess.opts.externs`) + /// + /// May be `None` if the crate wasn't used + pub fn resolved_extern_crate(&self, externs_name: Symbol) -> Option { + self.resolved_externs.get(&externs_name).copied() + } + pub(crate) fn iter_crate_data(&self) -> impl Iterator { self.metas .iter_enumerated() diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 27910ad0ab79..7bd2970eee70 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -395,6 +395,12 @@ flags to control that behavior. When the `--extern-html-root-url` flag is given one of your dependencies, rustdoc use that URL for those docs. Keep in mind that if those docs exist in the output directory, those local docs will still override this flag. +The names in this flag are first matched against the names given in the `--extern name=` flags, +which allows selecting between multiple crates with the same name (e.g. multiple versions of +the same crate). For transitive dependencies that haven't been loaded via an `--extern` flag, matching +falls backs to using crate names only, without ability to distinguish between multiple crates with +the same name. + ## `-Z force-unstable-if-unmarked` Using this flag looks like this: diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs index 5191120ebdb0..e28cc3a542e5 100644 --- a/src/librustdoc/formats/cache.rs +++ b/src/librustdoc/formats/cache.rs @@ -4,6 +4,7 @@ use rustc_ast::join_path_syms; use rustc_attr_data_structures::StabilityLevel; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; +use rustc_metadata::creader::CStore; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use tracing::debug; @@ -158,18 +159,33 @@ impl Cache { assert!(cx.external_traits.is_empty()); cx.cache.traits = mem::take(&mut krate.external_traits); + let render_options = &cx.render_options; + let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence; + let dst = &render_options.output; + + // Make `--extern-html-root-url` support the same names as `--extern` whenever possible + let cstore = CStore::from_tcx(tcx); + for (name, extern_url) in &render_options.extern_html_root_urls { + if let Some(crate_num) = cstore.resolved_extern_crate(Symbol::intern(name)) { + let e = ExternalCrate { crate_num }; + let location = e.location(Some(extern_url), extern_url_takes_precedence, dst, tcx); + cx.cache.extern_locations.insert(e.crate_num, location); + } + } + // Cache where all our extern crates are located - // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code + // This is also used in the JSON output. for &crate_num in tcx.crates(()) { let e = ExternalCrate { crate_num }; let name = e.name(tcx); - let render_options = &cx.render_options; - let extern_url = render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u); - let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence; - let dst = &render_options.output; - let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx); - cx.cache.extern_locations.insert(e.crate_num, location); + cx.cache.extern_locations.entry(e.crate_num).or_insert_with(|| { + // falls back to matching by crates' own names, because + // transitive dependencies and injected crates may be loaded without `--extern` + let extern_url = + render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u); + e.location(extern_url, extern_url_takes_precedence, dst, tcx) + }); cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module)); } From 71920e265caf363ba3408adc2744c0441ef58472 Mon Sep 17 00:00:00 2001 From: usamoi Date: Mon, 28 Jul 2025 20:41:14 +0800 Subject: [PATCH 132/201] fix `Atomic*::as_ptr` wording --- library/core/src/sync/atomic.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 57bea505433d..70c02ead3584 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1245,8 +1245,8 @@ impl AtomicBool { /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any - /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction in [Memory model for atomic accesses]. + /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the + /// requirements of the [memory model]. /// /// # Examples /// @@ -1265,7 +1265,7 @@ impl AtomicBool { /// # } /// ``` /// - /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + /// [memory model]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] @@ -2489,8 +2489,8 @@ impl AtomicPtr { /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any - /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction in [Memory model for atomic accesses]. + /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the + /// requirements of the [memory model]. /// /// # Examples /// @@ -2510,7 +2510,7 @@ impl AtomicPtr { /// } /// ``` /// - /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + /// [memory model]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] @@ -3623,8 +3623,8 @@ macro_rules! atomic_int { /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the /// atomic types work with interior mutability. All modifications of an atomic change the value /// through a shared reference, and can do so safely as long as they use atomic operations. Any - /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same - /// restriction in [Memory model for atomic accesses]. + /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the + /// requirements of the [memory model]. /// /// # Examples /// @@ -3645,7 +3645,7 @@ macro_rules! atomic_int { /// # } /// ``` /// - /// [Memory model for atomic accesses]: self#memory-model-for-atomic-accesses + /// [memory model]: self#memory-model-for-atomic-accesses #[inline] #[stable(feature = "atomic_as_ptr", since = "1.70.0")] #[rustc_const_stable(feature = "atomic_as_ptr", since = "1.70.0")] From f63f212a043dbc60fc28a4bf845662863d532fd7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 28 Jul 2025 18:53:24 +0530 Subject: [PATCH 133/201] use dry_run and verbose directly from exec_ctx --- src/bootstrap/src/core/download.rs | 57 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 745acb04275a..2011e3067c47 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -38,7 +38,7 @@ impl Config { } pub(crate) fn remove(&self, f: &Path) { - remove(self.dry_run(), f); + remove(&self.exec_ctx, f); } /// Create a temporary directory in `out` and return its path. @@ -74,13 +74,13 @@ impl Config { } fn unpack(&self, tarball: &Path, dst: &Path, pattern: &str) { - unpack(self.verbose > 0, tarball, dst, pattern); + unpack(&self.exec_ctx, tarball, dst, pattern); } /// Returns whether the SHA256 checksum of `path` matches `expected`. #[cfg(test)] pub(crate) fn verify(&self, path: &Path, expected: &str) -> bool { - verify(self.verbose > 0, self.dry_run(), path, expected) + verify(&self.exec_ctx, path, expected) } } @@ -414,12 +414,10 @@ pub(crate) struct DownloadContext<'a> { out: &'a Path, patch_binaries_for_nix: Option, exec_ctx: &'a ExecutionContext, - verbose: bool, stage0_metadata: &'a build_helper::stage0_parser::Stage0, llvm_assertions: bool, bootstrap_cache_path: &'a Option, is_running_on_ci: bool, - dry_run: bool, } impl<'a> AsRef> for DownloadContext<'a> { @@ -435,12 +433,10 @@ impl<'a> From<&'a Config> for DownloadContext<'a> { out: &value.out, patch_binaries_for_nix: value.patch_binaries_for_nix, exec_ctx: &value.exec_ctx, - verbose: value.verbose > 0, stage0_metadata: &value.stage0_metadata, llvm_assertions: value.llvm_assertions, bootstrap_cache_path: &value.bootstrap_cache_path, is_running_on_ci: value.is_running_on_ci, - dry_run: value.dry_run(), } } } @@ -508,7 +504,7 @@ pub(crate) fn maybe_download_rustfmt<'a>( let dwn_ctx = dwn_ctx.as_ref(); - if dwn_ctx.dry_run { + if dwn_ctx.exec_ctx.dry_run() { return Some(PathBuf::new()); } @@ -563,9 +559,9 @@ pub(crate) fn download_beta_toolchain<'a>(dwn_ctx: impl AsRef(dwn_ctx: impl AsRef>) { let dwn_ctx = dwn_ctx.as_ref(); - if dwn_ctx.verbose { + dwn_ctx.exec_ctx.verbose(|| { println!("downloading stage0 beta artifacts"); - } + }); let date = dwn_ctx.stage0_metadata.compiler.date.clone(); let version = dwn_ctx.stage0_metadata.compiler.version.clone(); @@ -634,8 +630,8 @@ fn download_toolchain<'a>( } } -pub(crate) fn remove(dry_run: bool, f: &Path) { - if dry_run { +pub(crate) fn remove(exec_ctx: &ExecutionContext, f: &Path) { + if exec_ctx.dry_run() { return; } fs::remove_file(f).unwrap_or_else(|_| panic!("failed to remove {f:?}")); @@ -757,7 +753,7 @@ fn download_component<'a>( ) { let dwn_ctx = dwn_ctx.as_ref(); - if dwn_ctx.dry_run { + if dwn_ctx.exec_ctx.dry_run() { return; } @@ -802,22 +798,22 @@ fn download_component<'a>( ); let sha256 = dwn_ctx.stage0_metadata.checksums_sha256.get(&url).expect(&error); if tarball.exists() { - if verify(dwn_ctx.verbose, dwn_ctx.dry_run, &tarball, sha256) { - unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); + if verify(dwn_ctx.exec_ctx, &tarball, sha256) { + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); return; } else { - if dwn_ctx.verbose { + dwn_ctx.exec_ctx.verbose(|| { println!( "ignoring cached file {} due to failed verification", tarball.display() ) - } - remove(dwn_ctx.dry_run, &tarball); + }); + remove(dwn_ctx.exec_ctx, &tarball); } } Some(sha256) } else if tarball.exists() { - unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); return; } else { None @@ -836,22 +832,22 @@ download-rustc = false } download_file(dwn_ctx, &format!("{base_url}/{url}"), &tarball, help_on_error); if let Some(sha256) = checksum - && !verify(dwn_ctx.verbose, dwn_ctx.dry_run, &tarball, sha256) + && !verify(dwn_ctx.exec_ctx, &tarball, sha256) { panic!("failed to verify {}", tarball.display()); } - unpack(dwn_ctx.verbose, &tarball, &bin_root, prefix); + unpack(dwn_ctx.exec_ctx, &tarball, &bin_root, prefix); } -pub(crate) fn verify(verbose: bool, dry_run: bool, path: &Path, expected: &str) -> bool { +pub(crate) fn verify(exec_ctx: &ExecutionContext, path: &Path, expected: &str) -> bool { use sha2::Digest; - if verbose { + exec_ctx.verbose(|| { println!("verifying {}", path.display()); - } + }); - if dry_run { + if exec_ctx.dry_run() { return false; } @@ -885,7 +881,7 @@ pub(crate) fn verify(verbose: bool, dry_run: bool, path: &Path, expected: &str) verified } -fn unpack(verbose: bool, tarball: &Path, dst: &Path, pattern: &str) { +fn unpack(exec_ctx: &ExecutionContext, tarball: &Path, dst: &Path, pattern: &str) { eprintln!("extracting {} to {}", tarball.display(), dst.display()); if !dst.exists() { t!(fs::create_dir_all(dst)); @@ -927,9 +923,10 @@ fn unpack(verbose: bool, tarball: &Path, dst: &Path, pattern: &str) { } short_path = short_path.strip_prefix(pattern).unwrap_or(short_path); let dst_path = dst.join(short_path); - if verbose { + + exec_ctx.verbose(|| { println!("extracting {} to {}", original_path.display(), dst.display()); - } + }); if !t!(member.unpack_in(dst)) { panic!("path traversal attack ??"); @@ -957,9 +954,9 @@ fn download_file<'a>( ) { let dwn_ctx = dwn_ctx.as_ref(); - if dwn_ctx.verbose { + dwn_ctx.exec_ctx.verbose(|| { println!("download {url}"); - } + }); // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/. let tempfile = tempdir(dwn_ctx.out).join(dest_path.file_name().unwrap()); // While bootstrap itself only supports http and https downloads, downstream forks might From d87b4f2c77add3404f7469ea72f1d7c51e2a29dc Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 25 Jul 2025 23:07:13 +0900 Subject: [PATCH 134/201] fix: Reject upvar scrutinees for `loop_match` --- compiler/rustc_mir_build/src/thir/cx/expr.rs | 21 +++-- tests/ui/loop-match/upvar-scrutinee.rs | 81 ++++++++++++++++++++ tests/ui/loop-match/upvar-scrutinee.stderr | 18 +++++ 3 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 tests/ui/loop-match/upvar-scrutinee.rs create mode 100644 tests/ui/loop-match/upvar-scrutinee.stderr diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 33bf4e3e29fb..16df58cd76df 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -955,9 +955,13 @@ impl<'tcx> ThirBuildCx<'tcx> { dcx.emit_fatal(LoopMatchBadRhs { span: block_body_expr.span }) }; - fn local(expr: &rustc_hir::Expr<'_>) -> Option { + fn local( + cx: &mut ThirBuildCx<'_>, + expr: &rustc_hir::Expr<'_>, + ) -> Option { if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind && let Res::Local(hir_id) = path.res + && !cx.is_upvar(hir_id) { return Some(hir_id); } @@ -965,11 +969,11 @@ impl<'tcx> ThirBuildCx<'tcx> { None } - let Some(scrutinee_hir_id) = local(scrutinee) else { + let Some(scrutinee_hir_id) = local(self, scrutinee) else { dcx.emit_fatal(LoopMatchInvalidMatch { span: scrutinee.span }) }; - if local(state) != Some(scrutinee_hir_id) { + if local(self, state) != Some(scrutinee_hir_id) { dcx.emit_fatal(LoopMatchInvalidUpdate { scrutinee: scrutinee.span, lhs: state.span, @@ -1260,10 +1264,7 @@ impl<'tcx> ThirBuildCx<'tcx> { fn convert_var(&mut self, var_hir_id: hir::HirId) -> ExprKind<'tcx> { // We want upvars here not captures. // Captures will be handled in MIR. - let is_upvar = self - .tcx - .upvars_mentioned(self.body_owner) - .is_some_and(|upvars| upvars.contains_key(&var_hir_id)); + let is_upvar = self.is_upvar(var_hir_id); debug!( "convert_var({:?}): is_upvar={}, body_owner={:?}", @@ -1443,6 +1444,12 @@ impl<'tcx> ThirBuildCx<'tcx> { } } + fn is_upvar(&mut self, var_hir_id: hir::HirId) -> bool { + self.tcx + .upvars_mentioned(self.body_owner) + .is_some_and(|upvars| upvars.contains_key(&var_hir_id)) + } + /// Converts a list of named fields (i.e., for struct-like struct/enum ADTs) into FieldExpr. fn field_refs(&mut self, fields: &'tcx [hir::ExprField<'tcx>]) -> Box<[FieldExpr]> { fields diff --git a/tests/ui/loop-match/upvar-scrutinee.rs b/tests/ui/loop-match/upvar-scrutinee.rs new file mode 100644 index 000000000000..a93e3a0e59ab --- /dev/null +++ b/tests/ui/loop-match/upvar-scrutinee.rs @@ -0,0 +1,81 @@ +#![allow(incomplete_features)] +#![feature(loop_match)] + +#[derive(Clone, Copy)] +enum State { + A, + B, +} + +fn main() { + let mut state = State::A; + + #[loop_match] + loop { + state = 'blk: { + match state { + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + + || { + #[loop_match] + loop { + state = 'blk: { + match state { + //~^ ERROR invalid match on `#[loop_match]` state + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; + + || { + let mut state = state; + #[loop_match] + loop { + state = 'blk: { + match state { + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; + + move || { + #[loop_match] + loop { + state = 'blk: { + match state { + //~^ ERROR invalid match on `#[loop_match]` state + State::A => { + #[const_continue] + break 'blk State::B; + } + State::B => { + return; + } + } + } + } + }; +} diff --git a/tests/ui/loop-match/upvar-scrutinee.stderr b/tests/ui/loop-match/upvar-scrutinee.stderr new file mode 100644 index 000000000000..b7a0a90193d7 --- /dev/null +++ b/tests/ui/loop-match/upvar-scrutinee.stderr @@ -0,0 +1,18 @@ +error: invalid match on `#[loop_match]` state + --> $DIR/upvar-scrutinee.rs:32:23 + | +LL | match state { + | ^^^^^ + | + = note: a local variable must be the scrutinee within a `#[loop_match]` + +error: invalid match on `#[loop_match]` state + --> $DIR/upvar-scrutinee.rs:68:23 + | +LL | match state { + | ^^^^^ + | + = note: a local variable must be the scrutinee within a `#[loop_match]` + +error: aborting due to 2 previous errors + From d409694c74d8e9606d9668e5ee78ce6ac364e4f7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Jul 2025 15:51:41 +0200 Subject: [PATCH 135/201] lookup_link_section: support arrays of function pointers --- src/tools/miri/src/helpers.rs | 17 +++++++++++++++-- src/tools/miri/tests/pass/shims/ctor.rs | 15 +++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 0c96ddf00db4..ab7e35710d34 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -1087,8 +1087,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "failed to evaluate static in required link_section: {def_id:?}\n{err:?}" ) }); - let val = this.read_immediate(&const_val)?; - array.push(val); + match const_val.layout.ty.kind() { + ty::FnPtr(..) => { + array.push(this.read_immediate(&const_val)?); + } + ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => { + let mut elems = this.project_array_fields(&const_val)?; + while let Some((_idx, elem)) = elems.next(this)? { + array.push(this.read_immediate(&elem)?); + } + } + _ => + throw_unsup_format!( + "only function pointers and arrays of function pointers are supported in well-known linker sections" + ), + } } interp_ok(()) })?; diff --git a/src/tools/miri/tests/pass/shims/ctor.rs b/src/tools/miri/tests/pass/shims/ctor.rs index b997d2386b82..a0fcdb1081e6 100644 --- a/src/tools/miri/tests/pass/shims/ctor.rs +++ b/src/tools/miri/tests/pass/shims/ctor.rs @@ -2,13 +2,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; static COUNT: AtomicUsize = AtomicUsize::new(0); -unsafe extern "C" fn ctor() { - COUNT.fetch_add(1, Ordering::Relaxed); +unsafe extern "C" fn ctor() { + COUNT.fetch_add(N, Ordering::Relaxed); } #[rustfmt::skip] macro_rules! ctor { - ($ident:ident = $ctor:ident) => { + ($ident:ident: $ty:ty = $ctor:expr) => { #[cfg_attr( all(any( target_os = "linux", @@ -33,14 +33,13 @@ macro_rules! ctor { link_section = "__DATA,__mod_init_func" )] #[used] - static $ident: unsafe extern "C" fn() = $ctor; + static $ident: $ty = $ctor; }; } -ctor! { CTOR1 = ctor } -ctor! { CTOR2 = ctor } -ctor! { CTOR3 = ctor } +ctor! { CTOR1: unsafe extern "C" fn() = ctor::<1> } +ctor! { CTOR2: [unsafe extern "C" fn(); 2] = [ctor::<2>, ctor::<3>] } fn main() { - assert_eq!(COUNT.load(Ordering::Relaxed), 3, "ctors did not run"); + assert_eq!(COUNT.load(Ordering::Relaxed), 6, "ctors did not run"); } From db42d5b36c804be21efd9c537a5a6771ec1c8201 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 28 Jul 2025 19:23:04 +0530 Subject: [PATCH 136/201] remove config wrappers of download_toolchain and maybe_download_fmt and during config parsing directly invoke cdownload methods --- src/bootstrap/src/core/config/config.rs | 18 +++++++++++++----- src/bootstrap/src/core/download.rs | 12 ------------ 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 6e04f1154243..90001f9ae318 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -45,7 +45,9 @@ use crate::core::config::{ DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, set, threads_from_config, }; -use crate::core::download::is_download_ci_available; +use crate::core::download::{ + DownloadContext, download_beta_toolchain, is_download_ci_available, maybe_download_rustfmt, +}; use crate::utils::channel; use crate::utils::exec::{ExecutionContext, command}; use crate::utils::helpers::{exe, get_host_target}; @@ -801,7 +803,8 @@ impl Config { } rustc } else { - config.download_beta_toolchain(); + let dwn_ctx = DownloadContext::from(&config); + download_beta_toolchain(dwn_ctx); config .out .join(config.host_target) @@ -827,7 +830,8 @@ impl Config { } cargo } else { - config.download_beta_toolchain(); + let dwn_ctx = DownloadContext::from(&config); + download_beta_toolchain(dwn_ctx); config.initial_sysroot.join("bin").join(exe("cargo", config.host_target)) }; @@ -994,8 +998,12 @@ impl Config { config.apply_dist_config(toml.dist); - config.initial_rustfmt = - if let Some(r) = rustfmt { Some(r) } else { config.maybe_download_rustfmt() }; + config.initial_rustfmt = if let Some(r) = rustfmt { + Some(r) + } else { + let dwn_ctx = DownloadContext::from(&config); + maybe_download_rustfmt(dwn_ctx) + }; if matches!(config.lld_mode, LldMode::SelfContained) && !config.lld_enabled diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs index 2011e3067c47..7ec6c62a07d0 100644 --- a/src/bootstrap/src/core/download.rs +++ b/src/bootstrap/src/core/download.rs @@ -128,13 +128,6 @@ impl Config { cargo_clippy } - /// NOTE: rustfmt is a completely different toolchain than the bootstrap compiler, so it can't - /// reuse target directories or artifacts - pub(crate) fn maybe_download_rustfmt(&self) -> Option { - let dwn_ctx: DownloadContext<'_> = self.into(); - maybe_download_rustfmt(dwn_ctx) - } - pub(crate) fn ci_rust_std_contents(&self) -> Vec { self.ci_component_contents(".rust-std-contents") } @@ -172,11 +165,6 @@ impl Config { ); } - pub(crate) fn download_beta_toolchain(&self) { - let dwn_ctx: DownloadContext<'_> = self.into(); - download_beta_toolchain(dwn_ctx); - } - fn download_toolchain( &self, version: &str, From c56f49dc34f43568122f057d72f3472b3fcd4d8e Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 28 Jul 2025 17:24:28 +0300 Subject: [PATCH 137/201] expand: Micro-optimize prelude injection Use `splice` to avoid shifting the other items twice. Put `extern crate std;` first so it's already resolved when we resolve `::std::prelude::rust_20XX`. --- compiler/rustc_builtin_macros/src/standard_library_imports.rs | 4 +--- tests/pretty/asm.pp | 4 ++-- tests/pretty/cast-lt.pp | 4 ++-- tests/pretty/dollar-crate.pp | 4 ++-- tests/pretty/expanded-and-path-remap-80832.pp | 4 ++-- tests/pretty/format-args-str-escape.pp | 4 ++-- tests/pretty/hir-delegation.pp | 4 ++-- tests/pretty/hir-fn-params.pp | 4 ++-- tests/pretty/hir-fn-variadic.pp | 4 ++-- tests/pretty/hir-if-else.pp | 4 ++-- tests/pretty/hir-lifetimes.pp | 4 ++-- tests/pretty/hir-pretty-attr.pp | 4 ++-- tests/pretty/hir-pretty-loop.pp | 4 ++-- tests/pretty/hir-struct-expr.pp | 4 ++-- tests/pretty/if-else.pp | 4 ++-- tests/pretty/issue-12590-c.pp | 4 ++-- tests/pretty/issue-4264.pp | 4 ++-- tests/pretty/issue-85089.pp | 4 ++-- tests/pretty/never-pattern.pp | 4 ++-- tests/pretty/pin-ergonomics-hir.pp | 4 ++-- tests/pretty/postfix-match/precedence.pp | 4 ++-- tests/pretty/shebang-at-top.pp | 4 ++-- tests/pretty/tests-are-sorted.pp | 4 ++-- tests/ui/asm/unpretty-expanded.stdout | 4 ++-- .../return-type-notation/unpretty-parenthesized.stdout | 4 ++-- tests/ui/codemap_tests/unicode.expanded.stdout | 4 ++-- tests/ui/const-generics/defaults/pretty-printing-ast.stdout | 4 ++-- tests/ui/deriving/built-in-proc-macro-scope.stdout | 4 ++-- tests/ui/deriving/deriving-all-codegen.stdout | 4 ++-- tests/ui/deriving/deriving-coerce-pointee-expanded.stdout | 4 ++-- tests/ui/deriving/proc-macro-attribute-mixing.stdout | 4 ++-- .../no_ice_for_partial_compiler_runs.stdout | 4 ++-- tests/ui/macros/genercs-in-path-with-prettry-hir.stdout | 4 ++-- .../non-consuming-methods-have-optimized-codegen.stdout | 4 ++-- tests/ui/match/issue-82392.stdout | 4 ++-- tests/ui/proc-macro/meta-macro-hygiene.stdout | 4 ++-- tests/ui/proc-macro/nonterminal-token-hygiene.stdout | 4 ++-- tests/ui/proc-macro/quote/debug.stdout | 4 ++-- tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout | 4 ++-- tests/ui/type-alias-impl-trait/issue-60662.stdout | 4 ++-- tests/ui/unpretty/bad-literal.stdout | 4 ++-- tests/ui/unpretty/debug-fmt-hir.stdout | 4 ++-- tests/ui/unpretty/deprecated-attr.stdout | 4 ++-- tests/ui/unpretty/diagnostic-attr.stdout | 4 ++-- tests/ui/unpretty/exhaustive-asm.expanded.stdout | 4 ++-- tests/ui/unpretty/exhaustive-asm.hir.stdout | 4 ++-- tests/ui/unpretty/exhaustive.expanded.stdout | 4 ++-- tests/ui/unpretty/exhaustive.hir.stdout | 4 ++-- tests/ui/unpretty/flattened-format-args.stdout | 4 ++-- tests/ui/unpretty/interpolation-expanded.stdout | 4 ++-- tests/ui/unpretty/let-else-hir.stdout | 4 ++-- tests/ui/unpretty/self-hir.stdout | 4 ++-- tests/ui/unpretty/unpretty-expr-fn-arg.stdout | 4 ++-- 53 files changed, 105 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs index 682e7c9b17ae..2068b5ca54dd 100644 --- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs +++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs @@ -47,8 +47,6 @@ pub fn inject( ast::ItemKind::ExternCrate(None, Ident::new(name, ident_span)), ); - krate.items.insert(0, item); - let root = (edition == Edition2015).then_some(kw::PathRoot); let import_path = root @@ -75,6 +73,6 @@ pub fn inject( }), ); - krate.items.insert(0, use_item); + krate.items.splice(0..0, [item, use_item]); krate.items.len() - orig_num_items } diff --git a/tests/pretty/asm.pp b/tests/pretty/asm.pp index e6c9545f51ee..dca28f99a37d 100644 --- a/tests/pretty/asm.pp +++ b/tests/pretty/asm.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-mode:expanded //@ pp-exact:asm.pp //@ only-x86_64 diff --git a/tests/pretty/cast-lt.pp b/tests/pretty/cast-lt.pp index f6155c9d827b..e82636edca7e 100644 --- a/tests/pretty/cast-lt.pp +++ b/tests/pretty/cast-lt.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:expanded //@ pp-exact:cast-lt.pp diff --git a/tests/pretty/dollar-crate.pp b/tests/pretty/dollar-crate.pp index 561a9500aaaa..31a55ec2bdaa 100644 --- a/tests/pretty/dollar-crate.pp +++ b/tests/pretty/dollar-crate.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:expanded //@ pp-exact:dollar-crate.pp diff --git a/tests/pretty/expanded-and-path-remap-80832.pp b/tests/pretty/expanded-and-path-remap-80832.pp index 5b3922bb3294..6206498e4a2b 100644 --- a/tests/pretty/expanded-and-path-remap-80832.pp +++ b/tests/pretty/expanded-and-path-remap-80832.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; // Test for issue 80832 // //@ pretty-mode:expanded diff --git a/tests/pretty/format-args-str-escape.pp b/tests/pretty/format-args-str-escape.pp index 277b608475cf..d0bd7cf0c72a 100644 --- a/tests/pretty/format-args-str-escape.pp +++ b/tests/pretty/format-args-str-escape.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:expanded //@ pp-exact:format-args-str-escape.pp diff --git a/tests/pretty/hir-delegation.pp b/tests/pretty/hir-delegation.pp index c0d724cccb51..f8ad02f2fccc 100644 --- a/tests/pretty/hir-delegation.pp +++ b/tests/pretty/hir-delegation.pp @@ -4,10 +4,10 @@ #![allow(incomplete_features)] #![feature(fn_delegation)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; fn b(e: C) { } diff --git a/tests/pretty/hir-fn-params.pp b/tests/pretty/hir-fn-params.pp index cfb33cc93eba..fb4ea0304e5a 100644 --- a/tests/pretty/hir-fn-params.pp +++ b/tests/pretty/hir-fn-params.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-fn-params.pp diff --git a/tests/pretty/hir-fn-variadic.pp b/tests/pretty/hir-fn-variadic.pp index 99919e7fc6b2..c0f5b7069a2d 100644 --- a/tests/pretty/hir-fn-variadic.pp +++ b/tests/pretty/hir-fn-variadic.pp @@ -3,10 +3,10 @@ //@ pp-exact:hir-fn-variadic.pp #![feature(c_variadic)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; extern "C" { unsafe fn foo(x: i32, va1: ...); diff --git a/tests/pretty/hir-if-else.pp b/tests/pretty/hir-if-else.pp index 4bccde663eb5..af5d31b07cb1 100644 --- a/tests/pretty/hir-if-else.pp +++ b/tests/pretty/hir-if-else.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-if-else.pp diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp index 1bb2f17cdfb4..00c052d3f798 100644 --- a/tests/pretty/hir-lifetimes.pp +++ b/tests/pretty/hir-lifetimes.pp @@ -5,10 +5,10 @@ // This tests the pretty-printing of lifetimes in lots of ways. #![allow(unused)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; struct Foo<'a> { x: &'a u32, diff --git a/tests/pretty/hir-pretty-attr.pp b/tests/pretty/hir-pretty-attr.pp index c780f8e3639f..01bfe2c09547 100644 --- a/tests/pretty/hir-pretty-attr.pp +++ b/tests/pretty/hir-pretty-attr.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-pretty-attr.pp diff --git a/tests/pretty/hir-pretty-loop.pp b/tests/pretty/hir-pretty-loop.pp index c07120273c90..a0830c5188ad 100644 --- a/tests/pretty/hir-pretty-loop.pp +++ b/tests/pretty/hir-pretty-loop.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-pretty-loop.pp diff --git a/tests/pretty/hir-struct-expr.pp b/tests/pretty/hir-struct-expr.pp index 177eb5e8631f..bb222dc2e5f1 100644 --- a/tests/pretty/hir-struct-expr.pp +++ b/tests/pretty/hir-struct-expr.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir //@ pp-exact:hir-struct-expr.pp diff --git a/tests/pretty/if-else.pp b/tests/pretty/if-else.pp index d4ff02c54415..f29b693e571e 100644 --- a/tests/pretty/if-else.pp +++ b/tests/pretty/if-else.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:expanded //@ pp-exact:if-else.pp diff --git a/tests/pretty/issue-12590-c.pp b/tests/pretty/issue-12590-c.pp index 691738a89ed8..0df095b0ee55 100644 --- a/tests/pretty/issue-12590-c.pp +++ b/tests/pretty/issue-12590-c.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:expanded //@ pp-exact:issue-12590-c.pp diff --git a/tests/pretty/issue-4264.pp b/tests/pretty/issue-4264.pp index f4b641335d1d..1344923f4c47 100644 --- a/tests/pretty/issue-4264.pp +++ b/tests/pretty/issue-4264.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-compare-only //@ pretty-mode:hir,typed //@ pp-exact:issue-4264.pp diff --git a/tests/pretty/issue-85089.pp b/tests/pretty/issue-85089.pp index 31c0f90bf27e..28a85bdf4ad8 100644 --- a/tests/pretty/issue-85089.pp +++ b/tests/pretty/issue-85089.pp @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; // Test to print lifetimes on HIR pretty-printing. //@ pretty-compare-only diff --git a/tests/pretty/never-pattern.pp b/tests/pretty/never-pattern.pp index 923ad9b82c7a..1ce332ea5064 100644 --- a/tests/pretty/never-pattern.pp +++ b/tests/pretty/never-pattern.pp @@ -7,10 +7,10 @@ #![allow(incomplete_features)] #![feature(never_patterns)] #![feature(never_type)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; fn f(x: Result) { _ = match x { Ok(x) => x, Err(!) , }; } diff --git a/tests/pretty/pin-ergonomics-hir.pp b/tests/pretty/pin-ergonomics-hir.pp index 58a1c62f712c..beca5988017d 100644 --- a/tests/pretty/pin-ergonomics-hir.pp +++ b/tests/pretty/pin-ergonomics-hir.pp @@ -4,10 +4,10 @@ #![feature(pin_ergonomics)] #![allow(dead_code, incomplete_features)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; use std::pin::Pin; diff --git a/tests/pretty/postfix-match/precedence.pp b/tests/pretty/postfix-match/precedence.pp index 2052b445a2b3..b6ff45daea12 100644 --- a/tests/pretty/postfix-match/precedence.pp +++ b/tests/pretty/postfix-match/precedence.pp @@ -1,10 +1,10 @@ #![feature(prelude_import)] #![no_std] #![feature(postfix_match)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; use std::ops::Add; diff --git a/tests/pretty/shebang-at-top.pp b/tests/pretty/shebang-at-top.pp index a27972526364..197def4a154b 100644 --- a/tests/pretty/shebang-at-top.pp +++ b/tests/pretty/shebang-at-top.pp @@ -1,10 +1,10 @@ #!/usr/bin/env rust #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ pretty-mode:expanded //@ pp-exact:shebang-at-top.pp //@ pretty-compare-only diff --git a/tests/pretty/tests-are-sorted.pp b/tests/pretty/tests-are-sorted.pp index d6a2c0ff9796..9e1566b2eff3 100644 --- a/tests/pretty/tests-are-sorted.pp +++ b/tests/pretty/tests-are-sorted.pp @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: --crate-type=lib --test --remap-path-prefix={{src-base}}/=/the/src/ --remap-path-prefix={{src-base}}\=/the/src/ //@ pretty-compare-only //@ pretty-mode:expanded diff --git a/tests/ui/asm/unpretty-expanded.stdout b/tests/ui/asm/unpretty-expanded.stdout index 7ba1702dfed8..7678f6bc3450 100644 --- a/tests/ui/asm/unpretty-expanded.stdout +++ b/tests/ui/asm/unpretty-expanded.stdout @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ needs-asm-support //@ check-pass //@ compile-flags: -Zunpretty=expanded diff --git a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout index 87667553837f..7499df5be5da 100644 --- a/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout +++ b/tests/ui/associated-type-bounds/return-type-notation/unpretty-parenthesized.stdout @@ -1,8 +1,8 @@ #![feature(prelude_import)] -#[prelude_import] -use std::prelude::rust_2021::*; #[macro_use] extern crate std; +#[prelude_import] +use std::prelude::rust_2021::*; //@ edition: 2021 //@ compile-flags: -Zunpretty=expanded //@ check-pass diff --git a/tests/ui/codemap_tests/unicode.expanded.stdout b/tests/ui/codemap_tests/unicode.expanded.stdout index c88035de0449..af375108b478 100644 --- a/tests/ui/codemap_tests/unicode.expanded.stdout +++ b/tests/ui/codemap_tests/unicode.expanded.stdout @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ revisions: normal expanded //@[expanded] check-pass //@[expanded]compile-flags: -Zunpretty=expanded diff --git a/tests/ui/const-generics/defaults/pretty-printing-ast.stdout b/tests/ui/const-generics/defaults/pretty-printing-ast.stdout index b6cb7fa09c88..030fcec9cf2a 100644 --- a/tests/ui/const-generics/defaults/pretty-printing-ast.stdout +++ b/tests/ui/const-generics/defaults/pretty-printing-ast.stdout @@ -6,10 +6,10 @@ //@ edition: 2015 #![crate_type = "lib"] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; trait Foo {} diff --git a/tests/ui/deriving/built-in-proc-macro-scope.stdout b/tests/ui/deriving/built-in-proc-macro-scope.stdout index 2697618ab003..4fbce5edb819 100644 --- a/tests/ui/deriving/built-in-proc-macro-scope.stdout +++ b/tests/ui/deriving/built-in-proc-macro-scope.stdout @@ -6,10 +6,10 @@ //@ edition:2015 #![feature(derive_coerce_pointee)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; #[macro_use] extern crate another_proc_macro; diff --git a/tests/ui/deriving/deriving-all-codegen.stdout b/tests/ui/deriving/deriving-all-codegen.stdout index fa8f249373d3..0e4bfa30257d 100644 --- a/tests/ui/deriving/deriving-all-codegen.stdout +++ b/tests/ui/deriving/deriving-all-codegen.stdout @@ -17,10 +17,10 @@ #![crate_type = "lib"] #![allow(dead_code)] #![allow(deprecated)] -#[prelude_import] -use std::prelude::rust_2021::*; #[macro_use] extern crate std; +#[prelude_import] +use std::prelude::rust_2021::*; // Empty struct. struct Empty; diff --git a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout index 84f8e9a3195a..89300a5c6d0c 100644 --- a/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout +++ b/tests/ui/deriving/deriving-coerce-pointee-expanded.stdout @@ -4,10 +4,10 @@ //@ compile-flags: -Zunpretty=expanded //@ edition: 2015 #![feature(derive_coerce_pointee)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; use std::marker::CoercePointee; pub trait MyTrait {} diff --git a/tests/ui/deriving/proc-macro-attribute-mixing.stdout b/tests/ui/deriving/proc-macro-attribute-mixing.stdout index faa9c0218a33..b81110682d68 100644 --- a/tests/ui/deriving/proc-macro-attribute-mixing.stdout +++ b/tests/ui/deriving/proc-macro-attribute-mixing.stdout @@ -12,10 +12,10 @@ //@ edition: 2015 #![feature(derive_coerce_pointee)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; #[macro_use] extern crate another_proc_macro; diff --git a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout index d63abea92302..80abac44ca84 100644 --- a/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout +++ b/tests/ui/lint/rfc-2383-lint-reason/no_ice_for_partial_compiler_runs.stdout @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; // This ensures that ICEs like rust#94953 don't happen //@ check-pass //@ compile-flags: -Z unpretty=expanded diff --git a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout index 6b0300132b5f..ba93384644d5 100644 --- a/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout +++ b/tests/ui/macros/genercs-in-path-with-prettry-hir.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ edition: 2015 diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout index 33193c78334c..e29655faabe5 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -5,10 +5,10 @@ //@ edition: 2015 #![feature(core_intrinsics, generic_assert)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; fn arbitrary_consuming_method_for_demonstration_purposes() { let elem = 1i32; diff --git a/tests/ui/match/issue-82392.stdout b/tests/ui/match/issue-82392.stdout index 3efc964e053b..d7eef0497392 100644 --- a/tests/ui/match/issue-82392.stdout +++ b/tests/ui/match/issue-82392.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; // https://github.com/rust-lang/rust/issues/82329 //@ compile-flags: -Zunpretty=hir,typed //@ check-pass diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index 91d16eca1b0a..452598c372c1 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -16,10 +16,10 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro // in the stdout #![no_std /* 0#0 */] -#[prelude_import /* 0#1 */] -use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#1 */; +#[prelude_import /* 0#1 */] +use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index 63741326e341..e10a5199f179 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -36,10 +36,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ #![feature /* 0#0 */(decl_macro)] #![no_std /* 0#0 */] -#[prelude_import /* 0#1 */] -use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#2 */; +#[prelude_import /* 0#1 */] +use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; diff --git a/tests/ui/proc-macro/quote/debug.stdout b/tests/ui/proc-macro/quote/debug.stdout index 3acb472d9c0f..77c52f02a33c 100644 --- a/tests/ui/proc-macro/quote/debug.stdout +++ b/tests/ui/proc-macro/quote/debug.stdout @@ -12,10 +12,10 @@ #![feature(proc_macro_quote)] #![crate_type = "proc-macro"] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; extern crate proc_macro; diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout index e2e45ae94ea6..66ba726fb9a4 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-pretty-check.stdout @@ -1,9 +1,9 @@ #![feature(prelude_import)] #![no_std] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[macro_use] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ check-pass //@ compile-flags: -Z unpretty=expanded //@ edition: 2015 diff --git a/tests/ui/type-alias-impl-trait/issue-60662.stdout b/tests/ui/type-alias-impl-trait/issue-60662.stdout index 52152a73affe..7ad29c88bcfe 100644 --- a/tests/ui/type-alias-impl-trait/issue-60662.stdout +++ b/tests/ui/type-alias-impl-trait/issue-60662.stdout @@ -3,10 +3,10 @@ //@ edition: 2015 #![feature(type_alias_impl_trait)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; trait Animal { } diff --git a/tests/ui/unpretty/bad-literal.stdout b/tests/ui/unpretty/bad-literal.stdout index ba8467359cd6..1f697aff27c9 100644 --- a/tests/ui/unpretty/bad-literal.stdout +++ b/tests/ui/unpretty/bad-literal.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-fail //@ edition: 2015 diff --git a/tests/ui/unpretty/debug-fmt-hir.stdout b/tests/ui/unpretty/debug-fmt-hir.stdout index 1d224c9e91ff..9c79421e32ab 100644 --- a/tests/ui/unpretty/debug-fmt-hir.stdout +++ b/tests/ui/unpretty/debug-fmt-hir.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/deprecated-attr.stdout b/tests/ui/unpretty/deprecated-attr.stdout index 0abeef6f61e0..26cc74c11604 100644 --- a/tests/ui/unpretty/deprecated-attr.stdout +++ b/tests/ui/unpretty/deprecated-attr.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/diagnostic-attr.stdout b/tests/ui/unpretty/diagnostic-attr.stdout index a1325c61ca73..4822cf4700b9 100644 --- a/tests/ui/unpretty/diagnostic-attr.stdout +++ b/tests/ui/unpretty/diagnostic-attr.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/exhaustive-asm.expanded.stdout b/tests/ui/unpretty/exhaustive-asm.expanded.stdout index 92829b0ab15b..9a58e4c2877b 100644 --- a/tests/ui/unpretty/exhaustive-asm.expanded.stdout +++ b/tests/ui/unpretty/exhaustive-asm.expanded.stdout @@ -1,8 +1,8 @@ #![feature(prelude_import)] -#[prelude_import] -use std::prelude::rust_2024::*; #[macro_use] extern crate std; +#[prelude_import] +use std::prelude::rust_2024::*; //@ revisions: expanded hir //@[expanded]compile-flags: -Zunpretty=expanded //@[expanded]check-pass diff --git a/tests/ui/unpretty/exhaustive-asm.hir.stdout b/tests/ui/unpretty/exhaustive-asm.hir.stdout index bbd846a88454..b33b38c2caba 100644 --- a/tests/ui/unpretty/exhaustive-asm.hir.stdout +++ b/tests/ui/unpretty/exhaustive-asm.hir.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use std::prelude::rust_2024::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use std::prelude::rust_2024::*; //@ revisions: expanded hir //@[expanded]compile-flags: -Zunpretty=expanded //@[expanded]check-pass diff --git a/tests/ui/unpretty/exhaustive.expanded.stdout b/tests/ui/unpretty/exhaustive.expanded.stdout index 53ca3c8e3915..6b08f3e1cd76 100644 --- a/tests/ui/unpretty/exhaustive.expanded.stdout +++ b/tests/ui/unpretty/exhaustive.expanded.stdout @@ -29,10 +29,10 @@ #![feature(try_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] -#[prelude_import] -use std::prelude::rust_2024::*; #[macro_use] extern crate std; +#[prelude_import] +use std::prelude::rust_2024::*; #[prelude_import] use self::prelude::*; diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout index 77807728c9d3..9cfa65f58012 100644 --- a/tests/ui/unpretty/exhaustive.hir.stdout +++ b/tests/ui/unpretty/exhaustive.hir.stdout @@ -28,10 +28,10 @@ #![feature(try_blocks)] #![feature(yeet_expr)] #![allow(incomplete_features)] -#[prelude_import] -use std::prelude::rust_2024::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use std::prelude::rust_2024::*; #[prelude_import] use self::prelude::*; diff --git a/tests/ui/unpretty/flattened-format-args.stdout b/tests/ui/unpretty/flattened-format-args.stdout index 3cd027346655..0792dc10e946 100644 --- a/tests/ui/unpretty/flattened-format-args.stdout +++ b/tests/ui/unpretty/flattened-format-args.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir -Zflatten-format-args=yes //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/interpolation-expanded.stdout b/tests/ui/unpretty/interpolation-expanded.stdout index d46b46b67f41..7284a89e7a9b 100644 --- a/tests/ui/unpretty/interpolation-expanded.stdout +++ b/tests/ui/unpretty/interpolation-expanded.stdout @@ -10,10 +10,10 @@ // synthesizing parentheses indiscriminately; only where necessary. #![feature(if_let_guard)] -#[prelude_import] -use std::prelude::rust_2024::*; #[macro_use] extern crate std; +#[prelude_import] +use std::prelude::rust_2024::*; macro_rules! expr { ($expr:expr) => { $expr }; } diff --git a/tests/ui/unpretty/let-else-hir.stdout b/tests/ui/unpretty/let-else-hir.stdout index a83790d8bee5..14270a572027 100644 --- a/tests/ui/unpretty/let-else-hir.stdout +++ b/tests/ui/unpretty/let-else-hir.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/self-hir.stdout b/tests/ui/unpretty/self-hir.stdout index 1eafc3c8b46b..b190565dcc47 100644 --- a/tests/ui/unpretty/self-hir.stdout +++ b/tests/ui/unpretty/self-hir.stdout @@ -1,7 +1,7 @@ -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; //@ compile-flags: -Zunpretty=hir //@ check-pass //@ edition: 2015 diff --git a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout index e9fd2222a8d2..c04909a73613 100644 --- a/tests/ui/unpretty/unpretty-expr-fn-arg.stdout +++ b/tests/ui/unpretty/unpretty-expr-fn-arg.stdout @@ -8,10 +8,10 @@ //@ compile-flags: -Zunpretty=hir,typed //@ edition: 2015 #![allow(dead_code)] -#[prelude_import] -use ::std::prelude::rust_2015::*; #[attr = MacroUse {arguments: UseAll}] extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; fn main() ({ } as ()) From 96aca2b442f42684f2deeaf2be560e9548864363 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 16:40:38 -0500 Subject: [PATCH 138/201] Remove TraitAlias from trait_of_item This is dead code. --- compiler/rustc_middle/src/ty/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4c95f0748d32..2e1be6e322f8 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1994,7 +1994,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn trait_of_item(self, def_id: DefId) -> Option { if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { let parent = self.parent(def_id); - if let DefKind::Trait | DefKind::TraitAlias = self.def_kind(parent) { + if let DefKind::Trait = self.def_kind(parent) { return Some(parent); } } From 0d7abc8df083296c29e0fb816c1c4b10e4cf6577 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 18:34:17 -0500 Subject: [PATCH 139/201] Introduce assoc_parent --- compiler/rustc_hir/src/def.rs | 4 ++++ compiler/rustc_middle/src/ty/mod.rs | 21 +++++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index ca57c4f31641..3fee9af01b36 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -295,6 +295,10 @@ impl DefKind { } } + pub fn is_assoc(self) -> bool { + matches!(self, DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy) + } + /// This is a "module" in name resolution sense. #[inline] pub fn is_module_like(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2e1be6e322f8..556065577700 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1988,29 +1988,22 @@ impl<'tcx> TyCtxt<'tcx> { self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id) } + /// If the given `DefId` is an associated item, returns the `DefId` of the parent trait or impl. + pub fn assoc_parent(self, def_id: DefId) -> Option { + self.def_kind(def_id).is_assoc().then(|| self.parent(def_id)) + } + /// If the given `DefId` describes an item belonging to a trait, /// returns the `DefId` of the trait that the trait item belongs to; /// otherwise, returns `None`. pub fn trait_of_item(self, def_id: DefId) -> Option { - if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { - let parent = self.parent(def_id); - if let DefKind::Trait = self.def_kind(parent) { - return Some(parent); - } - } - None + self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) } /// If the given `DefId` describes a method belonging to an impl, returns the /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. pub fn impl_of_method(self, def_id: DefId) -> Option { - if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { - let parent = self.parent(def_id); - if let DefKind::Impl { .. } = self.def_kind(parent) { - return Some(parent); - } - } - None + self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) } pub fn is_exportable(self, def_id: DefId) -> bool { From 172af038a733020ec487d57a593ddb27ce16c325 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 19:05:31 -0500 Subject: [PATCH 140/201] Rename trait_of_item -> trait_of_assoc --- .../rustc_borrowck/src/diagnostics/conflict_errors.rs | 2 +- compiler/rustc_borrowck/src/diagnostics/mod.rs | 2 +- compiler/rustc_const_eval/src/check_consts/check.rs | 2 +- compiler/rustc_const_eval/src/check_consts/qualifs.rs | 2 +- compiler/rustc_const_eval/src/interpret/call.rs | 2 +- .../src/errors/wrong_number_of_generic_args.rs | 2 +- compiler/rustc_hir_typeck/src/fallback.rs | 2 +- compiler/rustc_lint/src/noop_method_call.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 4 ++-- compiler/rustc_mir_transform/src/check_call_recursion.rs | 2 +- compiler/rustc_mir_transform/src/shim.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 2 +- compiler/rustc_passes/src/dead.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/transform.rs | 6 +++--- .../src/error_reporting/infer/need_type_info.rs | 2 +- .../src/error_reporting/traits/ambiguity.rs | 2 +- .../src/error_reporting/traits/suggestions.rs | 2 +- compiler/rustc_trait_selection/src/traits/mod.rs | 2 +- compiler/rustc_trait_selection/src/traits/project.rs | 2 +- compiler/rustc_ty_utils/src/instance.rs | 2 +- src/tools/clippy/clippy_lints/src/dereference.rs | 2 +- .../clippy/clippy_lints/src/loops/needless_range_loop.rs | 2 +- .../clippy/clippy_lints/src/methods/clone_on_copy.rs | 2 +- .../clippy_lints/src/methods/iter_overeager_cloned.rs | 4 ++-- .../clippy/clippy_lints/src/methods/manual_str_repeat.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/map_clone.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/open_options.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/str_splitn.rs | 6 +++--- .../src/methods/unnecessary_fallible_conversions.rs | 2 +- .../clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- .../clippy/clippy_lints/src/methods/useless_asref.rs | 2 +- src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs | 2 +- src/tools/clippy/clippy_lints/src/ranges.rs | 2 +- .../clippy/clippy_lints/src/unconditional_recursion.rs | 8 ++++---- src/tools/clippy/clippy_lints/src/unused_io_amount.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 4 ++-- src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs | 2 +- 37 files changed, 47 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index bfe806e8901f..be8b3f0bc1e3 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -2384,7 +2384,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { if let Some(body_expr) = finder.body_expr && let Some(loop_span) = finder.loop_span && let Some(def_id) = typeck_results.type_dependent_def_id(body_expr.hir_id) - && let Some(trait_did) = tcx.trait_of_item(def_id) + && let Some(trait_did) = tcx.trait_of_assoc(def_id) && tcx.is_diagnostic_item(sym::Iterator, trait_did) { if let Some(loop_bind) = finder.loop_bind { diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index ed4cd52ed9c8..56fdaf1c724a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -938,7 +938,7 @@ impl<'tcx> BorrowedContentSource<'tcx> { fn from_call(func: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { match *func.kind() { ty::FnDef(def_id, args) => { - let trait_id = tcx.trait_of_item(def_id)?; + let trait_id = tcx.trait_of_assoc(def_id)?; if tcx.is_lang_item(trait_id, LangItem::Deref) || tcx.is_lang_item(trait_id, LangItem::DerefMut) diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index 69637cadcc99..44e5d1d5ee7e 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -784,7 +784,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { self.revalidate_conditional_constness(callee, fn_args, *fn_span); // Attempting to call a trait method? - if let Some(trait_did) = tcx.trait_of_item(callee) { + if let Some(trait_did) = tcx.trait_of_assoc(callee) { // We can't determine the actual callee here, so we have to do different checks // than usual. diff --git a/compiler/rustc_const_eval/src/check_consts/qualifs.rs b/compiler/rustc_const_eval/src/check_consts/qualifs.rs index 166491b47a1f..faf41f1658b7 100644 --- a/compiler/rustc_const_eval/src/check_consts/qualifs.rs +++ b/compiler/rustc_const_eval/src/check_consts/qualifs.rs @@ -369,7 +369,7 @@ where assert!(promoted.is_none() || Q::ALLOW_PROMOTED); // Don't peek inside trait associated constants. - if promoted.is_none() && cx.tcx.trait_of_item(def).is_none() { + if promoted.is_none() && cx.tcx.trait_of_assoc(def).is_none() { let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def); if !Q::in_qualifs(&qualifs) { diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index bda00ea2fb88..5b3adba02659 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -725,7 +725,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) { let tcx = *self.tcx; - let trait_def_id = tcx.trait_of_item(def_id).unwrap(); + let trait_def_id = tcx.trait_of_assoc(def_id).unwrap(); let virtual_trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, virtual_instance.args); let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 2d60c9561a98..835f8e8cdaee 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -759,7 +759,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { &self, err: &mut Diag<'_, impl EmissionGuarantee>, ) { - let trait_ = match self.tcx.trait_of_item(self.def_id) { + let trait_ = match self.tcx.trait_of_assoc(self.def_id) { Some(def_id) => def_id, None => return, }; diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index 759b5d9550c0..9406697dfed6 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -694,7 +694,7 @@ impl<'tcx> Visitor<'tcx> for AnnotateUnitFallbackVisitor<'_, 'tcx> { // i.e. changing `Default::default()` to `<() as Default>::default()`. if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Def(DefKind::AssocFn, def_id) = path.res - && self.fcx.tcx.trait_of_item(def_id).is_some() + && self.fcx.tcx.trait_of_assoc(def_id).is_some() && let Some(args) = self.fcx.typeck_results.borrow().node_args_opt(expr.hir_id) && let self_ty = args.type_at(0) && let Some(vid) = self.fcx.root_vid(self_ty) diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index b7835e6c36ab..24682c4562a0 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { return; }; - let Some(trait_id) = cx.tcx.trait_of_item(did) else { return }; + let Some(trait_id) = cx.tcx.trait_of_assoc(did) else { return }; let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return }; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 556065577700..2e4bad290d7d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1996,7 +1996,7 @@ impl<'tcx> TyCtxt<'tcx> { /// If the given `DefId` describes an item belonging to a trait, /// returns the `DefId` of the trait that the trait item belongs to; /// otherwise, returns `None`. - pub fn trait_of_item(self, def_id: DefId) -> Option { + pub fn trait_of_assoc(self, def_id: DefId) -> Option { self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) } @@ -2174,7 +2174,7 @@ impl<'tcx> TyCtxt<'tcx> { #[inline] pub fn is_const_default_method(self, def_id: DefId) -> bool { - matches!(self.trait_of_item(def_id), Some(trait_id) if self.is_const_trait(trait_id)) + matches!(self.trait_of_assoc(def_id), Some(trait_id) if self.is_const_trait(trait_id)) } pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index cace4cd6bba5..dccfcc52b463 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -21,7 +21,7 @@ impl<'tcx> MirLint<'tcx> for CheckCallRecursion { if let DefKind::Fn | DefKind::AssocFn = tcx.def_kind(def_id) { // If this is trait/impl method, extract the trait's args. - let trait_args = match tcx.trait_of_item(def_id.to_def_id()) { + let trait_args = match tcx.trait_of_assoc(def_id.to_def_id()) { Some(trait_def_id) => { let trait_args_count = tcx.generics_of(trait_def_id).count(); &GenericArgs::identity_for_item(tcx, def_id)[..trait_args_count] diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index 6c65b072bec0..c687036f544d 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -75,7 +75,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id)) } ty::InstanceKind::FnPtrShim(def_id, ty) => { - let trait_ = tcx.trait_of_item(def_id).unwrap(); + let trait_ = tcx.trait_of_assoc(def_id).unwrap(); // Supports `Fn` or `async Fn` traits. let adjustment = match tcx .fn_trait_kind_from_def_id(trait_) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index f3e7d582781d..9f5807070a7b 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -650,7 +650,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( // its self-type. If the self-type does not provide a characteristic // DefId, we use the location of the impl after all. - if tcx.trait_of_item(def_id).is_some() { + if tcx.trait_of_assoc(def_id).is_some() { let self_ty = instance.args.type_at(0); // This is a default implementation of a trait method. return characteristic_def_id_of_type(self_ty).or(Some(def_id)); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index d987041fe0e0..ae379d53ad12 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -429,7 +429,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { Node::TraitItem(trait_item) => { // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); - if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { + if let Some(trait_id) = self.tcx.trait_of_assoc(trait_item_id) { self.check_def_id(trait_id); } intravisit::walk_trait_item(self, trait_item); diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index c69991f3fb20..6d42d954b2bd 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -339,7 +339,7 @@ pub(crate) fn transform_instance<'tcx>( } else if let ty::InstanceKind::Virtual(def_id, _) = instance.def { // Transform self into a trait object of the trait that defines the method for virtual // functions to match the type erasure done below. - let upcast_ty = match tcx.trait_of_item(def_id) { + let upcast_ty = match tcx.trait_of_assoc(def_id) { Some(trait_id) => trait_object_ty( tcx, ty::Binder::dummy(ty::TraitRef::from_method(tcx, trait_id, instance.args)), @@ -364,7 +364,7 @@ pub(crate) fn transform_instance<'tcx>( }; instance.args = tcx.mk_args_trait(self_ty, instance.args.into_iter().skip(1)); } else if let ty::InstanceKind::VTableShim(def_id) = instance.def - && let Some(trait_id) = tcx.trait_of_item(def_id) + && let Some(trait_id) = tcx.trait_of_assoc(def_id) { // Adjust the type ids of VTableShims to the type id expected in the call sites for the // entry in the vtable (i.e., by using the signature of the closure passed as an argument @@ -480,7 +480,7 @@ fn implemented_method<'tcx>( // Provided method in a `trait` block trait_method = trait_method_bound; method_id = instance.def_id(); - trait_id = tcx.trait_of_item(method_id)?; + trait_id = tcx.trait_of_assoc(method_id)?; trait_ref = ty::EarlyBinder::bind(TraitRef::from_method(tcx, trait_id, instance.args)); trait_id } else { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 1db05ced8d2f..022d549a9df8 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -1303,7 +1303,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { && let Some(args) = self.node_args_opt(expr.hir_id) && args.iter().any(|arg| self.generic_arg_contains_target(arg)) && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) - && self.tecx.tcx.trait_of_item(def_id).is_some() + && self.tecx.tcx.trait_of_assoc(def_id).is_some() && !has_impl_trait(def_id) // FIXME(fn_delegation): In delegation item argument spans are equal to last path // segment. This leads to ICE's when emitting `multipart_suggestion`. diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 98f67257fd13..cdf1402252aa 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -360,7 +360,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }, ] = path.segments && data.trait_ref.def_id == *trait_id - && self.tcx.trait_of_item(*item_id) == Some(*trait_id) + && self.tcx.trait_of_assoc(*item_id) == Some(*trait_id) && let None = self.tainted_by_errors() { let assoc_item = self.tcx.associated_item(item_id); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index c52487363664..c182fd99b17b 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -987,7 +987,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { else { return false; }; - if self.tcx.trait_of_item(did) != Some(clone_trait) { + if self.tcx.trait_of_assoc(did) != Some(clone_trait) { return false; } Some(ident.span) diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index 91c41544f78f..9b5e59ce0fdb 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -759,7 +759,7 @@ fn instantiate_and_check_impossible_predicates<'tcx>( // Specifically check trait fulfillment to avoid an error when trying to resolve // associated items. - if let Some(trait_def_id) = tcx.trait_of_item(key.0) { + if let Some(trait_def_id) = tcx.trait_of_assoc(key.0) { let trait_ref = ty::TraitRef::from_method(tcx, trait_def_id, key.1); predicates.push(trait_ref.upcast(tcx)); } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 28b5b7cf391e..581191b2036d 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1507,7 +1507,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); let item_def_id = obligation.predicate.def_id; - let trait_def_id = tcx.trait_of_item(item_def_id).unwrap(); + let trait_def_id = tcx.trait_of_assoc(item_def_id).unwrap(); let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) { let discriminant_def_id = diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 166e8f193429..e6c3568620b5 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -21,7 +21,7 @@ fn resolve_instance_raw<'tcx>( ) -> Result>, ErrorGuaranteed> { let PseudoCanonicalInput { typing_env, value: (def_id, args) } = key; - let result = if let Some(trait_def_id) = tcx.trait_of_item(def_id) { + let result = if let Some(trait_def_id) = tcx.trait_of_assoc(def_id) { debug!(" => associated item, attempting to find impl in typing_env {:#?}", typing_env); resolve_associated_item( tcx, diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 5099df3fa023..995a1209595e 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -364,7 +364,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take // priority. if let Some(fn_id) = typeck.type_dependent_def_id(hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && let arg_ty = cx.tcx.erase_regions(adjusted_ty) && let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let args = diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 972b0b110e0e..7bb684d65bb4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -317,7 +317,7 @@ impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> { .cx .typeck_results() .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| self.cx.tcx.trait_of_item(def_id)) + .and_then(|def_id| self.cx.tcx.trait_of_assoc(def_id)) && ((meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id))) && !self.check(args_1, args_0, expr) diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index 2ecf3eb89798..0a456d1057ad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -28,7 +28,7 @@ pub(super) fn check( if cx .typeck_results() .type_dependent_def_id(expr.hir_id) - .and_then(|id| cx.tcx.trait_of_item(id)) + .and_then(|id| cx.tcx.trait_of_assoc(id)) .zip(cx.tcx.lang_items().clone_trait()) .is_none_or(|(x, y)| x != y) { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs index f5fe4316eb0d..f851ebe91f37 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs @@ -44,9 +44,9 @@ pub(super) fn check<'tcx>( let typeck = cx.typeck_results(); if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator) && let Some(method_id) = typeck.type_dependent_def_id(expr.hir_id) - && cx.tcx.trait_of_item(method_id) == Some(iter_id) + && cx.tcx.trait_of_assoc(method_id) == Some(iter_id) && let Some(method_id) = typeck.type_dependent_def_id(cloned_call.hir_id) - && cx.tcx.trait_of_item(method_id) == Some(iter_id) + && cx.tcx.trait_of_assoc(method_id) == Some(iter_id) && let cloned_recv_ty = typeck.expr_ty_adjusted(cloned_recv) && let Some(iter_assoc_ty) = cx.get_associated_type(cloned_recv_ty, iter_id, sym::Item) && matches!(*iter_assoc_ty.kind(), ty::Ref(_, ty, _) if !is_copy(cx, ty)) diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs index 8167e4f96053..a811dd1cee18 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs @@ -59,7 +59,7 @@ pub(super) fn check( && is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String) && let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id) && let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator) - && cx.tcx.trait_of_item(take_id) == Some(iter_trait_id) + && cx.tcx.trait_of_assoc(take_id) == Some(iter_trait_id) && let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg) && let ctxt = collect_expr.span.ctxt() && ctxt == take_expr.span.ctxt() diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 333a33f7527d..d016ace28bd4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -69,7 +69,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_ hir::ExprKind::MethodCall(method, obj, [], _) => { if ident_eq(name, obj) && method.ident.name == sym::clone && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 9b5f138295c3..87e6cf021023 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -111,7 +111,7 @@ fn get_open_options( // This might be a user defined extension trait with a method like `truncate_write` // which would be a false positive if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(argument.hir_id) - && cx.tcx.trait_of_item(method_def_id).is_some() + && cx.tcx.trait_of_assoc(method_def_id).is_some() { return false; } diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 6f78d6c61281..51dd4ac313a6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -286,7 +286,7 @@ fn parse_iter_usage<'tcx>( let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; match (name.ident.name, args) { - (sym::next, []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), + (sym::next, []) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), (sym::next_tuple, []) => { return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did) && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind() @@ -303,7 +303,7 @@ fn parse_iter_usage<'tcx>( None }; }, - (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { + (sym::nth | sym::skip, [idx_expr]) if cx.tcx.trait_of_assoc(did) == Some(iter_id) => { if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) { let span = if name.ident.as_str() == "nth" { e.span @@ -312,7 +312,7 @@ fn parse_iter_usage<'tcx>( && next_name.ident.name == sym::next && next_expr.span.ctxt() == ctxt && let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id) - && cx.tcx.trait_of_item(next_id) == Some(iter_id) + && cx.tcx.trait_of_assoc(next_id) == Some(iter_id) { next_expr.span } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs index ce81282ddfeb..0ec2d8b4fc31 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs @@ -165,7 +165,7 @@ pub(super) fn check_method(cx: &LateContext<'_>, expr: &Expr<'_>) { pub(super) fn check_function(cx: &LateContext<'_>, expr: &Expr<'_>, callee: &Expr<'_>) { if let ExprKind::Path(ref qpath) = callee.kind && let Some(item_def_id) = cx.qpath_res(qpath, callee.hir_id).opt_def_id() - && let Some(trait_def_id) = cx.tcx.trait_of_item(item_def_id) + && let Some(trait_def_id) = cx.tcx.trait_of_assoc(item_def_id) { let qpath_spans = match qpath { QPath::Resolved(_, path) => { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 769526d131bf..a86428808216 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -734,7 +734,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && cx.tcx.trait_of_item(method_def_id).is_none() + && cx.tcx.trait_of_assoc(method_def_id).is_none() && let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow) && cx.tcx.predicates_of(method_def_id).predicates.iter().any(|(pred, _)| { if let ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index d30c12e0c483..d9cf8273fada 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -131,7 +131,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { hir::ExprKind::MethodCall(method, obj, [], _) => { if method.ident.name == sym::clone && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) // We check it's the `Clone` trait. && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index 9b2cfd91b853..22ec4fe60fb0 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -41,7 +41,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) ExprKind::MethodCall(_, arg, [], _) if typeck .type_dependent_def_id(expr.hir_id) - .and_then(|id| cx.tcx.trait_of_item(id)) + .and_then(|id| cx.tcx.trait_of_assoc(id)) .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))) => { (arg, arg.span) diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 9281678b3d83..03d00ba849f3 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -380,7 +380,7 @@ fn can_switch_ranges<'tcx>( if let ExprKind::MethodCall(_, receiver, _, _) = parent_expr.kind && receiver.hir_id == use_ctxt.child_id && let Some(method_did) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) - && let Some(trait_did) = cx.tcx.trait_of_item(method_did) + && let Some(trait_did) = cx.tcx.trait_of_assoc(method_did) && matches!( cx.tcx.get_diagnostic_name(trait_did), Some(sym::Iterator | sym::IntoIterator | sym::RangeBounds) diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index d321c48f6aff..dcddff557d1c 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -206,7 +206,7 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca let arg_ty = cx.typeck_results().expr_ty_adjusted(arg); if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && trait_id == trait_def_id && matches_ty(receiver_ty, arg_ty, self_arg, other_arg) { @@ -250,7 +250,7 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local let is_bad = match expr.kind { ExprKind::MethodCall(segment, _receiver, &[_arg], _) if segment.ident.name == name.name => { if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(trait_id) = cx.tcx.trait_of_item(fn_id) + && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id) && trait_id == trait_def_id { true @@ -318,7 +318,7 @@ where && let ExprKind::Path(qpath) = f.kind && is_default_method_on_current_ty(self.cx.tcx, qpath, self.implemented_ty_id) && let Some(method_def_id) = path_def_id(self.cx, f) - && let Some(trait_def_id) = self.cx.tcx.trait_of_item(method_def_id) + && let Some(trait_def_id) = self.cx.tcx.trait_of_assoc(method_def_id) && self.cx.tcx.is_diagnostic_item(sym::Default, trait_def_id) { span_error(self.cx, self.method_span, expr); @@ -426,7 +426,7 @@ fn check_from(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId if let Some((fn_def_id, node_args)) = fn_def_id_with_node_args(cx, expr) && let [s1, s2] = **node_args && let (Some(s1), Some(s2)) = (s1.as_type(), s2.as_type()) - && let Some(trait_def_id) = cx.tcx.trait_of_item(fn_def_id) + && let Some(trait_def_id) = cx.tcx.trait_of_assoc(fn_def_id) && cx.tcx.is_diagnostic_item(sym::Into, trait_def_id) && get_impl_trait_def_id(cx, method_def_id) == cx.tcx.get_diagnostic_item(sym::From) && s1 == sig.inputs()[0] diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 12cc10938993..bed5c0fc0150 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -300,7 +300,7 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option { }; if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id) - && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id) + && let Some(trait_def_id) = cx.tcx.trait_of_assoc(method_def_id) { if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) { match diag_name { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index ce5af4d2f482..211af4d5b089 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -349,7 +349,7 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool { /// Checks if the given method call expression calls an inherent method. pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { - cx.tcx.trait_of_item(method_id).is_none() + cx.tcx.trait_of_assoc(method_id).is_none() } else { false } @@ -367,7 +367,7 @@ pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbo /// Checks if a method is in a diagnostic item trait pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(trait_did) = cx.tcx.trait_of_item(def_id) { + if let Some(trait_did) = cx.tcx.trait_of_assoc(def_id) { return cx.tcx.is_diagnostic_item(diag_item, trait_did); } false diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index b3356450d387..11c17a77b154 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -420,7 +420,7 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo .lookup_const_stability(def_id) .or_else(|| { cx.tcx - .trait_of_item(def_id) + .trait_of_assoc(def_id) .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { From b43164cef643957f9e1cd20dffdbb736f3f3c298 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 19:09:05 -0500 Subject: [PATCH 141/201] Rename impl_of_method -> impl_of_assoc --- .../rustc_borrowck/src/diagnostics/mutability_errors.rs | 6 +++--- compiler/rustc_borrowck/src/type_check/mod.rs | 2 +- compiler/rustc_codegen_llvm/src/debuginfo/mod.rs | 2 +- compiler/rustc_codegen_ssa/src/back/symbol_export.rs | 2 +- compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 2 +- compiler/rustc_lint/src/map_unit_fn.rs | 2 +- compiler/rustc_lint/src/pass_by_value.rs | 2 +- compiler/rustc_lint/src/types.rs | 2 +- compiler/rustc_middle/src/middle/privacy.rs | 2 +- compiler/rustc_middle/src/ty/mod.rs | 2 +- compiler/rustc_mir_transform/src/check_call_recursion.rs | 2 +- compiler/rustc_mir_transform/src/check_packed_ref.rs | 2 +- compiler/rustc_mir_transform/src/coverage/query.rs | 2 +- compiler/rustc_monomorphize/src/partitioning.rs | 2 +- compiler/rustc_passes/src/dead.rs | 2 +- .../src/cfi/typeid/itanium_cxx_abi/transform.rs | 2 +- src/tools/clippy/clippy_lints/src/assigning_clones.rs | 2 +- .../clippy/clippy_lints/src/casts/cast_ptr_alignment.rs | 2 +- .../src/casts/confusing_method_to_numeric_cast.rs | 2 +- .../clippy/clippy_lints/src/implicit_saturating_sub.rs | 4 ++-- .../clippy/clippy_lints/src/methods/bytes_count_to_len.rs | 2 +- .../methods/case_sensitive_file_extension_comparisons.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/get_first.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/map_clone.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/open_options.rs | 2 +- .../clippy_lints/src/methods/path_buf_push_overwrite.rs | 2 +- .../clippy_lints/src/methods/stable_sort_primitive.rs | 2 +- .../clippy/clippy_lints/src/methods/suspicious_splitn.rs | 2 +- .../clippy/clippy_lints/src/methods/unnecessary_sort_by.rs | 2 +- .../clippy/clippy_lints/src/methods/unnecessary_to_owned.rs | 2 +- src/tools/clippy/clippy_lints/src/methods/useless_asref.rs | 2 +- .../clippy/clippy_lints/src/methods/vec_resize_to_zero.rs | 2 +- src/tools/clippy/clippy_lints/src/operators/op_ref.rs | 2 +- .../clippy/clippy_lints/src/return_self_not_must_use.rs | 2 +- src/tools/clippy/clippy_lints/src/unused_io_amount.rs | 2 +- src/tools/clippy/clippy_utils/src/eager_or_lazy.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 4 ++-- 41 files changed, 45 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a06540f83250..5d9416b59fce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -682,7 +682,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } let my_def = self.body.source.def_id(); let Some(td) = - self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) + self.infcx.tcx.impl_of_assoc(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) else { return (false, false, None); }; @@ -880,7 +880,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let opt_suggestions = tcx .typeck(path_segment.hir_id.owner.def_id) .type_dependent_def_id(expr.hir_id) - .and_then(|def_id| tcx.impl_of_method(def_id)) + .and_then(|def_id| tcx.impl_of_assoc(def_id)) .map(|def_id| tcx.associated_items(def_id)) .map(|assoc_items| { assoc_items @@ -1056,7 +1056,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { .tcx .typeck(path_segment.hir_id.owner.def_id) .type_dependent_def_id(cur_expr.hir_id) - .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id)) + .and_then(|def_id| self.infcx.tcx.impl_of_assoc(def_id)) .map(|def_id| self.infcx.tcx.associated_items(def_id)) .map(|assoc_items| { assoc_items.filter_by_name_unhygienic(sym::iter_mut).peekable() diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index f363ef0a5a98..f5fedbf95c1c 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1761,7 +1761,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); assert!(!matches!( - tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)), + tcx.impl_of_assoc(def_id).map(|imp| tcx.def_kind(imp)), Some(DefKind::Impl { of_trait: true }) )); self.prove_predicates( diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 5ca2505cec43..6cbf2dbf7d3f 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -533,7 +533,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // First, let's see if this is a method within an inherent impl. Because // if yes, we want to make the result subroutine DIE a child of the // subroutine's self-type. - if let Some(impl_def_id) = cx.tcx.impl_of_method(instance.def_id()) { + if let Some(impl_def_id) = cx.tcx.impl_of_assoc(instance.def_id()) { // If the method does *not* belong to a trait, proceed if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions( diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 2f5eca2d6b23..297bdec2bc21 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -86,7 +86,7 @@ fn reachable_non_generics_provider(tcx: TyCtxt<'_>, _: LocalCrate) -> DefIdMap {} - DefKind::AssocFn if tcx.impl_of_method(def_id.to_def_id()).is_some() => {} + DefKind::AssocFn if tcx.impl_of_assoc(def_id.to_def_id()).is_some() => {} _ => return None, }; diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 287a5532f016..f73442fdebd4 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -755,7 +755,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let limit = if candidates.len() == 5 { 5 } else { 4 }; for (index, &item) in candidates.iter().take(limit).enumerate() { - let impl_ = tcx.impl_of_method(item).unwrap(); + let impl_ = tcx.impl_of_assoc(item).unwrap(); let note_span = if item.is_local() { Some(tcx.def_span(item)) diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs index a8803158889f..34210137bde1 100644 --- a/compiler/rustc_lint/src/map_unit_fn.rs +++ b/compiler/rustc_lint/src/map_unit_fn.rs @@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for MapUnitFn { fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) { return cx.tcx.type_of(impl_id).skip_binder().is_slice(); } diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index d3b3b55dd4c4..a3cf3d568b1c 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -24,7 +24,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) { + if let Some(impl_did) = cx.tcx.impl_of_assoc(ty.hir_id.owner.to_def_id()) { if cx.tcx.impl_trait_ref(impl_did).is_some() { return; } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index fc9d795cb231..b0afc333ebe2 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1904,7 +1904,7 @@ impl InvalidAtomicOrdering { if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind && recognized_names.contains(&method_path.ident.name) && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id) + && let Some(impl_did) = cx.tcx.impl_of_assoc(m_def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() // skip extension traits, only lint functions from the standard library && cx.tcx.trait_id_of_impl(impl_did).is_none() diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 03549091d62d..785ddd1ee298 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -182,7 +182,7 @@ impl EffectiveVisibilities { // don't check this condition for them. let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. }); let is_associated_item_in_trait_impl = tcx - .impl_of_method(def_id.to_def_id()) + .impl_of_assoc(def_id.to_def_id()) .and_then(|impl_id| tcx.trait_id_of_impl(impl_id)) .is_some(); if !is_impl && !is_associated_item_in_trait_impl { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 2e4bad290d7d..1119e60be85b 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -2002,7 +2002,7 @@ impl<'tcx> TyCtxt<'tcx> { /// If the given `DefId` describes a method belonging to an impl, returns the /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. - pub fn impl_of_method(self, def_id: DefId) -> Option { + pub fn impl_of_assoc(self, def_id: DefId) -> Option { self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) } diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index dccfcc52b463..6d61ac2dd803 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -44,7 +44,7 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { // First check if `body` is an `fn drop()` of `Drop` if let DefKind::AssocFn = tcx.def_kind(def_id) && let Some(trait_ref) = - tcx.impl_of_method(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) + tcx.impl_of_assoc(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) && tcx.is_lang_item(trait_ref.instantiate_identity().def_id, LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity() diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index e9b85ba6e9db..dcb812c78993 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -40,7 +40,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { if context.is_borrow() && util::is_disaligned(self.tcx, self.body, self.typing_env, *place) { let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = self.tcx.impl_of_method(def_id) + if let Some(impl_def_id) = self.tcx.impl_of_assoc(def_id) && self.tcx.is_builtin_derived(impl_def_id) { // If we ever reach here it means that the generated derive diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 986c001de5e8..551f720c869e 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -35,7 +35,7 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // Don't instrument functions with `#[automatically_derived]` on their // enclosing impl block, on the assumption that most users won't care about // coverage for derived impls. - if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) + if let Some(impl_of) = tcx.impl_of_assoc(def_id.to_def_id()) && tcx.is_automatically_derived(impl_of) { trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 9f5807070a7b..cee15e0f6969 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -656,7 +656,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( return characteristic_def_id_of_type(self_ty).or(Some(def_id)); } - if let Some(impl_def_id) = tcx.impl_of_method(def_id) { + if let Some(impl_def_id) = tcx.impl_of_assoc(def_id) { if tcx.sess.opts.incremental.is_some() && tcx .trait_id_of_impl(impl_def_id) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index ae379d53ad12..a90d1af87ca1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -368,7 +368,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). fn should_ignore_item(&mut self, def_id: DefId) -> bool { - if let Some(impl_of) = self.tcx.impl_of_method(def_id) { + if let Some(impl_of) = self.tcx.impl_of_assoc(def_id) { if !self.tcx.is_automatically_derived(impl_of) { return false; } diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 6d42d954b2bd..52717d73b8ae 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -466,7 +466,7 @@ fn implemented_method<'tcx>( let method_id; let trait_id; let trait_method; - let ancestor = if let Some(impl_id) = tcx.impl_of_method(instance.def_id()) { + let ancestor = if let Some(impl_id) = tcx.impl_of_assoc(instance.def_id()) { // Implementation in an `impl` block trait_ref = tcx.impl_trait_ref(impl_id)?; let impl_method = tcx.associated_item(instance.def_id()); diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 8b8b42bbf722..52287be34c78 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones { // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs)) - && let Some(resolved_impl) = cx.tcx.impl_of_method(resolved_fn.def_id()) + && let Some(resolved_impl) = cx.tcx.impl_of_assoc(resolved_fn.def_id()) // Derived forms don't implement `clone_from`/`clone_into`. // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 && !cx.tcx.is_builtin_derived(resolved_impl) diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs index e4dafde0f9df..a1543cabd2f9 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -63,7 +63,7 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { ExprKind::MethodCall(name, self_arg, ..) if self_arg.hir_id == e.hir_id => { if matches!(name.ident.name, sym::read_unaligned | sym::write_unaligned) && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id) - && let Some(def_id) = cx.tcx.impl_of_method(def_id) + && let Some(def_id) = cx.tcx.impl_of_assoc(def_id) && cx.tcx.type_of(def_id).instantiate_identity().is_raw_ptr() { true diff --git a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs index 849de22cfbaa..73347e7141eb 100644 --- a/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/confusing_method_to_numeric_cast.rs @@ -37,7 +37,7 @@ fn get_const_name_and_ty_name( } else { return None; } - } else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id) + } else if let Some(impl_id) = cx.tcx.impl_of_assoc(method_def_id) && let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity()) && matches!( method_name, diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index c743501da253..c634c12e1877 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -339,7 +339,7 @@ fn check_with_condition<'tcx>( ExprKind::Path(QPath::TypeRelative(_, name)) => { if name.ident.name == sym::MIN && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(const_id) && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() { @@ -350,7 +350,7 @@ fn check_with_condition<'tcx>( if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind && name.ident.name == sym::min_value && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(func_id) && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() { diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs index a9f6a41c2357..b8cc5ddd845c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>( bytes_recv: &'tcx hir::Expr<'_>, ) { if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(bytes_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(bytes_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs() && (ty.is_str() || is_type_lang_item(cx, ty, hir::LangItem::String)) diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 292fa08b5984..6f9702f6c6c3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( } if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_str() && let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), diff --git a/src/tools/clippy/clippy_lints/src/methods/get_first.rs b/src/tools/clippy/clippy_lints/src/methods/get_first.rs index f4465e654c2e..2e1d71ce284d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_first.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_first.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( arg: &'tcx hir::Expr<'_>, ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && let identity = cx.tcx.type_of(impl_id).instantiate_identity() && let hir::ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs index 9724463f0c08..efa8cee58df7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs @@ -50,7 +50,7 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: h sym::to_path_buf => is_diag_item_method(cx, method_def_id, sym::Path), sym::to_vec => cx .tcx - .impl_of_method(method_def_id) + .impl_of_assoc(method_def_id) .filter(|&impl_did| { cx.tcx.type_of(impl_did).instantiate_identity().is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none() }) diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index c286c5faaed3..077957fa44dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( map_expr: &'tcx Expr<'_>, ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Option) && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr) diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index d016ace28bd4..748be9bfcc62 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -23,7 +23,7 @@ fn should_run_lint(cx: &LateContext<'_>, e: &hir::Expr<'_>, method_id: DefId) -> return true; } // We check if it's an `Option` or a `Result`. - if let Some(id) = cx.tcx.impl_of_method(method_id) { + if let Some(id) = cx.tcx.impl_of_assoc(method_id) { let identity = cx.tcx.type_of(id).instantiate_identity(); if !is_type_diagnostic_item(cx, identity, sym::Option) && !is_type_diagnostic_item(cx, identity, sym::Result) { return false; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs index 5d0d4dae35fa..41beda9c5cb4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs @@ -8,7 +8,7 @@ use super::MAP_ERR_IGNORE; pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Result) && let ExprKind::Closure(&Closure { capture_clause: CaptureBy::Ref, diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs index 320523aceb67..4235af882b0c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs @@ -13,7 +13,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &' && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv)) && ref_depth >= 1 && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index 87e6cf021023..37a8e25bef96 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -18,7 +18,7 @@ fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_open_options(cx, cx.tcx.type_of(impl_id).instantiate_identity()) { let mut options = Vec::new(); diff --git a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs index 38d9c5f16778..32752ef7435f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs @@ -11,7 +11,7 @@ use super::PATH_BUF_PUSH_OVERWRITE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::PathBuf) && let ExprKind::Lit(lit) = arg.kind && let LitKind::Str(ref path_lit, _) = lit.node diff --git a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs index aef14435d8af..17d1a6abde0a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs +++ b/src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs @@ -9,7 +9,7 @@ use super::STABLE_SORT_PRIMITIVE; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_slice() && let Some(slice_type) = is_slice_of_primitives(cx, recv) { diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs index f8b6d4349fbe..9876681ddbb3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_splitn.rs @@ -10,7 +10,7 @@ use super::SUSPICIOUS_SPLITN; pub(super) fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &Expr<'_>, self_arg: &Expr<'_>, count: u128) { if count <= 1 && let Some(call_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(call_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(call_id) && cx.tcx.impl_trait_ref(impl_id).is_none() && let self_ty = cx.tcx.type_of(impl_id).instantiate_identity() && (self_ty.is_slice() || self_ty.is_str()) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index dbff08bc51c9..1de9f6ab4970 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -114,7 +114,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && cx.tcx.type_of(impl_id).instantiate_identity().is_slice() && let ExprKind::Closure(&Closure { body, .. }) = arg.kind && let closure_body = cx.tcx.hir_body(body) diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a86428808216..54f45263275c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -694,7 +694,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx sym::to_string => cx.tcx.is_diagnostic_item(sym::to_string_method, method_def_id), sym::to_vec => cx .tcx - .impl_of_method(method_def_id) + .impl_of_assoc(method_def_id) .filter(|&impl_did| cx.tcx.type_of(impl_did).instantiate_identity().is_slice()) .is_some(), _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index d9cf8273fada..38fad239f679 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -79,7 +79,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo applicability, ); } - } else if let Some(impl_id) = cx.tcx.impl_of_method(def_id) + } else if let Some(impl_id) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def() && matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Option | sym::Result)) { diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs index 5ea4ada128a2..bfb481f4fc09 100644 --- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -18,7 +18,7 @@ pub(super) fn check<'tcx>( name_span: Span, ) { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(method_id) + && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id) && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Vec) && let ExprKind::Lit(Spanned { node: LitKind::Int(Pu128(0), _), diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index 21e1ab0f4f27..0a1f2625f4cf 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -179,7 +179,7 @@ fn in_impl<'tcx>( bin_op: DefId, ) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> { if let Some(block) = get_enclosing_block(cx, e.hir_id) - && let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id()) + && let Some(impl_def_id) = cx.tcx.impl_of_assoc(block.hir_id.owner.to_def_id()) && let item = cx.tcx.hir_expect_item(impl_def_id.expect_local()) && let ItemKind::Impl(item) = &item.kind && let Some(of_trait) = &item.of_trait diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 25929b853af8..3497216d1c56 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for ReturnSelfNotMustUse { ) { if matches!(kind, FnKind::Method(_, _)) // We are only interested in methods, not in functions or associated functions. - && let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id()) + && let Some(impl_def) = cx.tcx.impl_of_assoc(fn_def.to_def_id()) // We don't want this method to be te implementation of a trait because the // `#[must_use]` should be put on the trait definition directly. && cx.tcx.trait_id_of_impl(impl_def).is_none() diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index bed5c0fc0150..f3cd3f1bb286 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { /// get desugared to match. fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) { let fn_def_id = block.hir_id.owner.to_def_id(); - if let Some(impl_id) = cx.tcx.impl_of_method(fn_def_id) + if let Some(impl_id) = cx.tcx.impl_of_assoc(fn_def_id) && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id) { // We don't want to lint inside io::Read or io::Write implementations, as the author has more diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 9d38672efada..eb3f442ac754 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -51,7 +51,7 @@ impl ops::BitOrAssign for EagernessSuggestion { fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let ty = match cx.tcx.impl_of_method(fn_id) { + let ty = match cx.tcx.impl_of_assoc(fn_id) { Some(id) => cx.tcx.type_of(id).instantiate_identity(), None => return Lazy, }; diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 211af4d5b089..67e09e772a77 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -357,7 +357,7 @@ pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { /// Checks if a method is defined in an impl of a diagnostic item pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool { - if let Some(impl_did) = cx.tcx.impl_of_method(def_id) + if let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { return cx.tcx.is_diagnostic_item(diag_item, adt.did()); @@ -620,7 +620,7 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath< if let QPath::TypeRelative(_, method) = path && method.ident.name == sym::new - && let Some(impl_did) = cx.tcx.impl_of_method(def_id) + && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id) && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() { return std_types_symbols.iter().any(|&symbol| { From cdcfdd1a1b3f267a98c693fc03b4411934b60674 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 24 Jul 2025 19:01:22 -0500 Subject: [PATCH 142/201] Tweak docs --- compiler/rustc_middle/src/ty/mod.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 1119e60be85b..bb70c61cd141 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1993,15 +1993,14 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(def_id).is_assoc().then(|| self.parent(def_id)) } - /// If the given `DefId` describes an item belonging to a trait, - /// returns the `DefId` of the trait that the trait item belongs to; - /// otherwise, returns `None`. + /// If the given `DefId` is an associated item of a trait, + /// returns the `DefId` of the trait; otherwise, returns `None`. pub fn trait_of_assoc(self, def_id: DefId) -> Option { self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) } - /// If the given `DefId` describes a method belonging to an impl, returns the - /// `DefId` of the impl that the method belongs to; otherwise, returns `None`. + /// If the given `DefId` is an associated item of an impl, + /// returns the `DefId` of the impl; otherwise returns `None`. pub fn impl_of_assoc(self, def_id: DefId) -> Option { self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) } From 73751a04910d08052bf6b825f8829cacba86c648 Mon Sep 17 00:00:00 2001 From: joboet Date: Sat, 26 Jul 2025 14:29:00 +0200 Subject: [PATCH 143/201] thread name in stack overflow message --- library/std/src/sys/pal/hermit/thread.rs | 6 ++++- library/std/src/sys/pal/itron/thread.rs | 6 ++++- library/std/src/sys/pal/sgx/thread.rs | 6 ++++- library/std/src/sys/pal/teeos/thread.rs | 6 ++++- library/std/src/sys/pal/uefi/thread.rs | 6 ++++- .../std/src/sys/pal/unix/stack_overflow.rs | 23 ++++++++++-------- library/std/src/sys/pal/unix/thread.rs | 24 +++++++++++++------ library/std/src/sys/pal/unsupported/thread.rs | 6 ++++- library/std/src/sys/pal/wasi/thread.rs | 4 ++-- .../std/src/sys/pal/wasm/atomics/thread.rs | 6 ++++- library/std/src/sys/pal/windows/thread.rs | 6 ++++- library/std/src/sys/pal/xous/thread.rs | 6 ++++- library/std/src/thread/mod.rs | 2 +- tests/ui/runtime/out-of-stack.rs | 12 +++++++--- 14 files changed, 87 insertions(+), 32 deletions(-) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 9bc5a16b8002..95fe4f902d33 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -58,7 +58,11 @@ impl Thread { } } - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { unsafe { Thread::new_with_coreid(stack, p, -1 /* = no specific core */) } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index 813e1cbcd58f..0d28051fcc46 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -86,7 +86,11 @@ impl Thread { /// # Safety /// /// See `thread::Builder::spawn_unchecked` for safety requirements. - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let inner = Box::new(ThreadInner { start: UnsafeCell::new(ManuallyDrop::new(p)), lifecycle: AtomicUsize::new(LIFECYCLE_INIT), diff --git a/library/std/src/sys/pal/sgx/thread.rs b/library/std/src/sys/pal/sgx/thread.rs index 85f6dcd96b4a..a236c3627060 100644 --- a/library/std/src/sys/pal/sgx/thread.rs +++ b/library/std/src/sys/pal/sgx/thread.rs @@ -96,7 +96,11 @@ pub mod wait_notify { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let mut queue_lock = task_queue::lock(); unsafe { usercalls::launch_thread()? }; let (task, handle) = task_queue::Task::new(p); diff --git a/library/std/src/sys/pal/teeos/thread.rs b/library/std/src/sys/pal/teeos/thread.rs index b9cdc7a2a58b..a91d95626e70 100644 --- a/library/std/src/sys/pal/teeos/thread.rs +++ b/library/std/src/sys/pal/teeos/thread.rs @@ -22,7 +22,11 @@ unsafe extern "C" { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; diff --git a/library/std/src/sys/pal/uefi/thread.rs b/library/std/src/sys/pal/uefi/thread.rs index e4776ec42fbb..75c364362b2d 100644 --- a/library/std/src/sys/pal/uefi/thread.rs +++ b/library/std/src/sys/pal/uefi/thread.rs @@ -11,7 +11,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index a3be2cdf738f..d89100e69196 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -8,8 +8,8 @@ pub struct Handler { } impl Handler { - pub unsafe fn new() -> Handler { - make_handler(false) + pub unsafe fn new(thread_name: Option>) -> Handler { + make_handler(false, thread_name) } fn null() -> Handler { @@ -72,7 +72,6 @@ mod imp { use crate::sync::OnceLock; use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; - use crate::thread::with_current_name; use crate::{io, mem, panic, ptr}; // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages @@ -158,13 +157,12 @@ mod imp { if !NEED_ALTSTACK.load(Ordering::Relaxed) { // haven't set up our sigaltstack yet NEED_ALTSTACK.store(true, Ordering::Release); - let handler = unsafe { make_handler(true) }; + let handler = unsafe { make_handler(true, None) }; MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed); mem::forget(handler); if let Some(guard_page_range) = guard_page_range.take() { - let thread_name = with_current_name(|name| name.map(Box::from)); - set_current_info(guard_page_range, thread_name); + set_current_info(guard_page_range, Some(Box::from("main"))); } } @@ -230,14 +228,13 @@ mod imp { /// # Safety /// Mutates the alternate signal stack #[forbid(unsafe_op_in_unsafe_fn)] - pub unsafe fn make_handler(main_thread: bool) -> Handler { + pub unsafe fn make_handler(main_thread: bool, thread_name: Option>) -> Handler { if !NEED_ALTSTACK.load(Ordering::Acquire) { return Handler::null(); } if !main_thread { if let Some(guard_page_range) = unsafe { current_guard() } { - let thread_name = with_current_name(|name| name.map(Box::from)); set_current_info(guard_page_range, thread_name); } } @@ -634,7 +631,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(_main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + _main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { super::Handler::null() } @@ -717,7 +717,10 @@ mod imp { pub unsafe fn cleanup() {} - pub unsafe fn make_handler(main_thread: bool) -> super::Handler { + pub unsafe fn make_handler( + main_thread: bool, + _thread_name: Option>, + ) -> super::Handler { if !main_thread { reserve_stack(); } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index e4f5520d8a33..7f6440152d4b 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -22,6 +22,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 256 * 1024; #[cfg(any(target_os = "espidf", target_os = "nuttx"))] pub const DEFAULT_MIN_STACK_SIZE: usize = 0; // 0 indicates that the stack size configured in the ESP-IDF/NuttX menuconfig system should be used +struct ThreadData { + name: Option>, + f: Box, +} + pub struct Thread { id: libc::pthread_t, } @@ -34,8 +39,12 @@ unsafe impl Sync for Thread {} impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { - let p = Box::into_raw(Box::new(p)); + pub unsafe fn new( + stack: usize, + name: Option<&str>, + f: Box, + ) -> io::Result { + let data = Box::into_raw(Box::new(ThreadData { name: name.map(Box::from), f })); let mut native: libc::pthread_t = mem::zeroed(); let mut attr: mem::MaybeUninit = mem::MaybeUninit::uninit(); assert_eq!(libc::pthread_attr_init(attr.as_mut_ptr()), 0); @@ -73,7 +82,7 @@ impl Thread { }; } - let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, p as *mut _); + let ret = libc::pthread_create(&mut native, attr.as_ptr(), thread_start, data as *mut _); // Note: if the thread creation fails and this assert fails, then p will // be leaked. However, an alternative design could cause double-free // which is clearly worse. @@ -82,19 +91,20 @@ impl Thread { return if ret != 0 { // The thread failed to start and as a result p was not consumed. Therefore, it is // safe to reconstruct the box so that it gets deallocated. - drop(Box::from_raw(p)); + drop(Box::from_raw(data)); Err(io::Error::from_raw_os_error(ret)) } else { Ok(Thread { id: native }) }; - extern "C" fn thread_start(main: *mut libc::c_void) -> *mut libc::c_void { + extern "C" fn thread_start(data: *mut libc::c_void) -> *mut libc::c_void { unsafe { + let data = Box::from_raw(data as *mut ThreadData); // Next, set up our stack overflow handler which may get triggered if we run // out of stack. - let _handler = stack_overflow::Handler::new(); + let _handler = stack_overflow::Handler::new(data.name); // Finally, let's run some code. - Box::from_raw(main as *mut Box)(); + (data.f)(); } ptr::null_mut() } diff --git a/library/std/src/sys/pal/unsupported/thread.rs b/library/std/src/sys/pal/unsupported/thread.rs index 8a3119fa292d..5a1e3fde9860 100644 --- a/library/std/src/sys/pal/unsupported/thread.rs +++ b/library/std/src/sys/pal/unsupported/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 64 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/wasi/thread.rs b/library/std/src/sys/pal/wasi/thread.rs index 5f21a553673a..a46c74630c97 100644 --- a/library/std/src/sys/pal/wasi/thread.rs +++ b/library/std/src/sys/pal/wasi/thread.rs @@ -73,7 +73,7 @@ impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements cfg_if::cfg_if! { if #[cfg(target_feature = "atomics")] { - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new(stack: usize, _name: Option<&str>, p: Box) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut native: libc::pthread_t = unsafe { mem::zeroed() }; let mut attr: libc::pthread_attr_t = unsafe { mem::zeroed() }; @@ -120,7 +120,7 @@ impl Thread { } } } else { - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new(_stack: usize, _name: Option<&str>, _p: Box) -> io::Result { crate::sys::unsupported() } } diff --git a/library/std/src/sys/pal/wasm/atomics/thread.rs b/library/std/src/sys/pal/wasm/atomics/thread.rs index 44ce3eab109f..ebfabaafc794 100644 --- a/library/std/src/sys/pal/wasm/atomics/thread.rs +++ b/library/std/src/sys/pal/wasm/atomics/thread.rs @@ -10,7 +10,11 @@ pub const DEFAULT_MIN_STACK_SIZE: usize = 1024 * 1024; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(_stack: usize, _p: Box) -> io::Result { + pub unsafe fn new( + _stack: usize, + _name: Option<&str>, + _p: Box, + ) -> io::Result { unsupported() } diff --git a/library/std/src/sys/pal/windows/thread.rs b/library/std/src/sys/pal/windows/thread.rs index 147851717553..b45f76fb546f 100644 --- a/library/std/src/sys/pal/windows/thread.rs +++ b/library/std/src/sys/pal/windows/thread.rs @@ -20,7 +20,11 @@ pub struct Thread { impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); // CreateThread rounds up values for the stack size to the nearest page size (at least 4kb). diff --git a/library/std/src/sys/pal/xous/thread.rs b/library/std/src/sys/pal/xous/thread.rs index 1b344e984dc3..f2404a62abf9 100644 --- a/library/std/src/sys/pal/xous/thread.rs +++ b/library/std/src/sys/pal/xous/thread.rs @@ -20,7 +20,11 @@ pub const GUARD_PAGE_SIZE: usize = 4096; impl Thread { // unsafe: see thread::Builder::spawn_unchecked for safety requirements - pub unsafe fn new(stack: usize, p: Box) -> io::Result { + pub unsafe fn new( + stack: usize, + _name: Option<&str>, + p: Box, + ) -> io::Result { let p = Box::into_raw(Box::new(p)); let mut stack_size = crate::cmp::max(stack, MIN_STACK_SIZE); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 6075173db47f..62ecacccd2ea 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -595,7 +595,7 @@ impl Builder { // Similarly, the `sys` implementation must guarantee that no references to the closure // exist after the thread has terminated, which is signaled by `Thread::join` // returning. - native: unsafe { imp::Thread::new(stack_size, main)? }, + native: unsafe { imp::Thread::new(stack_size, my_thread.name(), main)? }, thread: my_thread, packet: my_packet, }) diff --git a/tests/ui/runtime/out-of-stack.rs b/tests/ui/runtime/out-of-stack.rs index 6be34afb5603..913d3637c8f2 100644 --- a/tests/ui/runtime/out-of-stack.rs +++ b/tests/ui/runtime/out-of-stack.rs @@ -19,7 +19,7 @@ extern crate libc; use std::env; use std::hint::black_box; use std::process::Command; -use std::thread; +use std::thread::Builder; fn silent_recurse() { let buf = [0u8; 1000]; @@ -56,9 +56,9 @@ fn main() { } else if args.len() > 1 && args[1] == "loud" { loud_recurse(); } else if args.len() > 1 && args[1] == "silent-thread" { - thread::spawn(silent_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(silent_recurse).unwrap().join(); } else if args.len() > 1 && args[1] == "loud-thread" { - thread::spawn(loud_recurse).join(); + Builder::new().name("ferris".to_string()).spawn(loud_recurse).unwrap().join(); } else { let mut modes = vec![ "silent-thread", @@ -82,6 +82,12 @@ fn main() { let error = String::from_utf8_lossy(&silent.stderr); assert!(error.contains("has overflowed its stack"), "missing overflow message: {}", error); + + if mode.contains("thread") { + assert!(error.contains("ferris"), "missing thread name: {}", error); + } else { + assert!(error.contains("main"), "missing thread name: {}", error); + } } } } From 2db97b2f7d877e5db2c892b42ed4deb8d829af14 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 28 Jul 2025 16:09:47 +0000 Subject: [PATCH 144/201] Account for .yield in illegal postfix operator message --- compiler/rustc_parse/src/parser/expr.rs | 6 +++++- tests/ui/coroutine/postfix-yield-after-cast.rs | 10 ++++++++++ tests/ui/coroutine/postfix-yield-after-cast.stderr | 13 +++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/ui/coroutine/postfix-yield-after-cast.rs create mode 100644 tests/ui/coroutine/postfix-yield-after-cast.stderr diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 3d89530f9143..54d8a7910258 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -785,9 +785,13 @@ impl<'a> Parser<'a> { ExprKind::Call(_, _) => "a function call", ExprKind::Await(_, _) => "`.await`", ExprKind::Use(_, _) => "`.use`", + ExprKind::Yield(YieldKind::Postfix(_)) => "`.yield`", ExprKind::Match(_, _, MatchKind::Postfix) => "a postfix match", ExprKind::Err(_) => return Ok(with_postfix), - _ => unreachable!("parse_dot_or_call_expr_with_ shouldn't produce this"), + _ => unreachable!( + "did not expect {:?} as an illegal postfix operator following cast", + with_postfix.kind + ), } ); let mut err = self.dcx().struct_span_err(span, msg); diff --git a/tests/ui/coroutine/postfix-yield-after-cast.rs b/tests/ui/coroutine/postfix-yield-after-cast.rs new file mode 100644 index 000000000000..472efb9f5138 --- /dev/null +++ b/tests/ui/coroutine/postfix-yield-after-cast.rs @@ -0,0 +1,10 @@ +// Regression test for . + +#![feature(yield_expr, coroutines)] + +fn main() { + #[coroutine] || { + 0 as u8.yield + //~^ ERROR cast cannot be followed by `.yield` + }; +} diff --git a/tests/ui/coroutine/postfix-yield-after-cast.stderr b/tests/ui/coroutine/postfix-yield-after-cast.stderr new file mode 100644 index 000000000000..a4de064fdf87 --- /dev/null +++ b/tests/ui/coroutine/postfix-yield-after-cast.stderr @@ -0,0 +1,13 @@ +error: cast cannot be followed by `.yield` + --> $DIR/postfix-yield-after-cast.rs:7:9 + | +LL | 0 as u8.yield + | ^^^^^^^ + | +help: try surrounding the expression in parentheses + | +LL | (0 as u8).yield + | + + + +error: aborting due to 1 previous error + From c5b4606d2a2070f1e7d79622d6be83138b54de31 Mon Sep 17 00:00:00 2001 From: apiraino Date: Mon, 28 Jul 2025 15:16:00 +0200 Subject: [PATCH 145/201] Enable t-compiler backport nomination --- triagebot.toml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 894f56df7419..e1b4983adf7b 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -50,6 +50,21 @@ remove_labels = ["S-waiting-on-author"] # Those labels are added when PR author requests a review from an assignee add_labels = ["S-waiting-on-review"] +# [backport.*] sections autonominate pull requests for a backport +# see: https://forge.rust-lang.org/triagebot/backport.html + +[backport.t-compiler-beta-backport] +# The pull request MUST have one of these labels +required-pr-labels = ["T-compiler"] +# The regression MUST have this label +required-issue-label = "regression-from-stable-to-beta" +# if the above conditions matches, the PR will receive these labels +add-labels = ["beta-nominated"] + +[backport.t-compiler-stable-backport] +required-pr-labels = ["T-compiler"] +required-issue-label = "regression-from-stable-to-stable" +add-labels = ["stable-nominated"] # ------------------------------------------------------------------------------ # Ping groups From c071101da73c417612155eb44e4cf4b9b3ee7499 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Mon, 28 Jul 2025 22:23:56 +0530 Subject: [PATCH 146/201] make sure to populate DownloadState dependencies before its initialization in config parsing --- src/bootstrap/src/core/config/config.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 90001f9ae318..9644ade00b34 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -797,6 +797,11 @@ impl Config { ); } + config.patch_binaries_for_nix = patch_binaries_for_nix; + config.bootstrap_cache_path = bootstrap_cache_path; + config.llvm_assertions = + toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false)); + config.initial_rustc = if let Some(rustc) = rustc { if !flags_skip_stage0_validation { config.check_stage0_version(&rustc, "rustc"); @@ -867,7 +872,6 @@ impl Config { config.reuse = reuse.map(PathBuf::from); config.submodules = submodules; config.android_ndk = android_ndk; - config.bootstrap_cache_path = bootstrap_cache_path; set(&mut config.low_priority, low_priority); set(&mut config.compiler_docs, compiler_docs); set(&mut config.library_docs_private_items, library_docs_private_items); @@ -886,7 +890,6 @@ impl Config { set(&mut config.local_rebuild, local_rebuild); set(&mut config.print_step_timings, print_step_timings); set(&mut config.print_step_rusage, print_step_rusage); - config.patch_binaries_for_nix = patch_binaries_for_nix; config.verbose = cmp::max(config.verbose, flags_verbose as usize); @@ -895,9 +898,6 @@ impl Config { config.apply_install_config(toml.install); - config.llvm_assertions = - toml.llvm.as_ref().is_some_and(|llvm| llvm.assertions.unwrap_or(false)); - let file_content = t!(fs::read_to_string(config.src.join("src/ci/channel"))); let ci_channel = file_content.trim_end(); From 86bef358fa5eb22dd863f1fc31aaac789735d433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 28 Jul 2025 19:24:45 +0200 Subject: [PATCH 147/201] Configure triagebot to reopen bot PRs --- src/tools/rust-analyzer/triagebot.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/triagebot.toml b/src/tools/rust-analyzer/triagebot.toml index c9862495bc0c..27fdb672455b 100644 --- a/src/tools/rust-analyzer/triagebot.toml +++ b/src/tools/rust-analyzer/triagebot.toml @@ -28,3 +28,6 @@ labels = ["has-merge-commits", "S-waiting-on-author"] # Prevents mentions in commits to avoid users being spammed [no-mentions] + +# Automatically close and reopen PRs made by bots to run CI on them +[bot-pull-requests] From cde861ef363b9121335fba830e2151557dcab985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 28 Jul 2025 20:30:37 +0300 Subject: [PATCH 148/201] Format and bump rustc crates --- src/tools/rust-analyzer/Cargo.lock | 32 +++++++++---------- src/tools/rust-analyzer/Cargo.toml | 8 ++--- .../crates/hir-ty/src/layout/adt.rs | 6 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index c19e8471647f..7d03300c2216 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1259,7 +1259,7 @@ dependencies = [ "expect-test", "intern", "parser", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "rustc-hash 2.1.1", "smallvec", "span", @@ -1495,7 +1495,7 @@ dependencies = [ "drop_bomb", "edition", "expect-test", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "rustc-literal-escaper", "stdx", "tracing", @@ -1605,7 +1605,7 @@ dependencies = [ "object", "paths", "proc-macro-test", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "span", "syntax-bridge", "tt", @@ -1748,9 +1748,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb01e1fec578003c85481c1cad4ff8cd8195b07c2dc85ae3f716108507ae15d5" +checksum = "f18c877575c259d127072e9bfc41d985202262fb4d6bfdae3d1252147c2562c2" dependencies = [ "bitflags 2.9.1", "ra-ap-rustc_hashes", @@ -1760,18 +1760,18 @@ dependencies = [ [[package]] name = "ra-ap-rustc_hashes" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0ec056e72a472ffef8761ce96ece6c626eb07368c09d0105b6df30d27d07673" +checksum = "2439ed1df3472443133b66949f81080dff88089b42f825761455463709ee1cad" dependencies = [ "rustc-stable-hash", ] [[package]] name = "ra-ap-rustc_index" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcdd1001db0295e59052e9f53aeda588bbe81e362534f4687d41bd44777b5a7" +checksum = "57a24fe0be21be1f8ebc21dcb40129214fb4cefb0f2753f3d46b6dbe656a1a45" dependencies = [ "ra-ap-rustc_index_macros", "smallvec", @@ -1779,9 +1779,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728d64dd98e25530b32e3f7c7c1e844e52722b269360daa1cdeba9dff9727a26" +checksum = "844a27ddcad0116facae2df8e741fd788662cf93dc13029cd864f2b8013b81f9" dependencies = [ "proc-macro2", "quote", @@ -1801,9 +1801,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "415f0821f512608d825b3215489a6a6a2c18ed9f0045953d514e7ec23d4b90ab" +checksum = "2b734cfcb577d09877799a22742f1bd398be6c00bc428d9de56d48d11ece5771" dependencies = [ "memchr", "unicode-properties", @@ -1822,9 +1822,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.122.0" +version = "0.123.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4657fcfdfe06e2a02ec8180d4e7c95aecf4811ba50367e363d1a2300b7623284" +checksum = "75b0ee1f059b9dea0818c6c7267478926eee95ba4c7dcf89c8db32fa165d3904" dependencies = [ "ra-ap-rustc_index", "rustc-hash 2.1.1", @@ -2590,7 +2590,7 @@ version = "0.0.0" dependencies = [ "arrayvec", "intern", - "ra-ap-rustc_lexer 0.122.0", + "ra-ap-rustc_lexer 0.123.0", "stdx", "text-size", ] diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 87202e86d3f1..e7cf0212bf2a 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -89,11 +89,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } edition = { path = "./crates/edition", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.122", default-features = false } +ra-ap-rustc_lexer = { version = "0.123", default-features = false } ra-ap-rustc_parse_format = { version = "0.121", default-features = false } -ra-ap-rustc_index = { version = "0.122", default-features = false } -ra-ap-rustc_abi = { version = "0.122", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.122", default-features = false } +ra-ap-rustc_index = { version = "0.123", default-features = false } +ra-ap-rustc_abi = { version = "0.123", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.123", default-features = false } # local crates that aren't published to crates.io. These should not have versions. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs index 372a9dfc43d2..3f310c26ec14 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/adt.rs @@ -3,9 +3,9 @@ use std::{cmp, ops::Bound}; use hir_def::{ + AdtId, VariantId, layout::{Integer, ReprOptions, TargetDataLayout}, signatures::{StructFlags, VariantFields}, - AdtId, VariantId, }; use intern::sym; use rustc_index::IndexVec; @@ -13,9 +13,9 @@ use smallvec::SmallVec; use triomphe::Arc; use crate::{ - db::HirDatabase, - layout::{field_ty, Layout, LayoutError}, Substitution, TraitEnvironment, + db::HirDatabase, + layout::{Layout, LayoutError, field_ty}, }; use super::LayoutCx; From 5185a665a977dd6b57c76ffaaa8becd57e569c6f Mon Sep 17 00:00:00 2001 From: binarycat Date: Fri, 25 Jul 2025 14:02:01 -0500 Subject: [PATCH 149/201] tidy: increase performance of auto extra checks feature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jakub Beránek --- src/tools/tidy/src/extra_checks/mod.rs | 30 ++++++++------ src/tools/tidy/src/lib.rs | 55 ++++++++++++++++++-------- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs index 8121eb057db5..f90f716cd950 100644 --- a/src/tools/tidy/src/extra_checks/mod.rs +++ b/src/tools/tidy/src/extra_checks/mod.rs @@ -86,7 +86,7 @@ fn check_impl( std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1"); // Split comma-separated args up - let lint_args = match extra_checks { + let mut lint_args = match extra_checks { Some(s) => s .strip_prefix("--extra-checks=") .unwrap() @@ -99,11 +99,7 @@ fn check_impl( }) .filter_map(|(res, src)| match res { Ok(arg) => { - if arg.is_inactive_auto(ci_info) { - None - } else { - Some(arg) - } + Some(arg) } Err(err) => { // only warn because before bad extra checks would be silently ignored. @@ -114,6 +110,11 @@ fn check_impl( .collect(), None => vec![], }; + if lint_args.iter().any(|ck| ck.auto) { + crate::files_modified_batch_filter(ci_info, &mut lint_args, |ck, path| { + ck.is_non_auto_or_matches(path) + }); + } macro_rules! extra_check { ($lang:ident, $kind:ident) => { @@ -721,10 +722,10 @@ impl ExtraCheckArg { self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true) } - /// Returns `true` if this is an auto arg and the relevant files are not modified. - fn is_inactive_auto(&self, ci_info: &CiInfo) -> bool { + /// Returns `false` if this is an auto arg and the passed filename does not trigger the auto rule + fn is_non_auto_or_matches(&self, filepath: &str) -> bool { if !self.auto { - return false; + return true; } let ext = match self.lang { ExtraCheckLang::Py => ".py", @@ -732,12 +733,15 @@ impl ExtraCheckArg { ExtraCheckLang::Shell => ".sh", ExtraCheckLang::Js => ".js", ExtraCheckLang::Spellcheck => { - return !crate::files_modified(ci_info, |s| { - SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir)) - }); + for dir in SPELLCHECK_DIRS { + if Path::new(filepath).starts_with(dir) { + return true; + } + } + return false; } }; - !crate::files_modified(ci_info, |s| s.ends_with(ext)) + filepath.ends_with(ext) } fn has_supported_kind(&self) -> bool { diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 4f9fb308a866..4ea9d051ddb5 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -125,40 +125,61 @@ pub fn git_diff>(base_commit: &str, extra_arg: S) -> Option bool) -> bool { +/// Similar to `files_modified`, but only involves a single call to `git`. +/// +/// removes all elements from `items` that do not cause any match when `pred` is called with the list of modifed files. +/// +/// if in CI, no elements will be removed. +pub fn files_modified_batch_filter( + ci_info: &CiInfo, + items: &mut Vec, + pred: impl Fn(&T, &str) -> bool, +) { if CiEnv::is_ci() { // assume everything is modified on CI because we really don't want false positives there. - return true; + return; } let Some(base_commit) = &ci_info.base_commit else { eprintln!("No base commit, assuming all files are modified"); - return true; + return; }; match crate::git_diff(base_commit, "--name-status") { Some(output) => { - let modified_files = output.lines().filter_map(|ln| { - let (status, name) = ln - .trim_end() - .split_once('\t') - .expect("bad format from `git diff --name-status`"); - if status == "M" { Some(name) } else { None } - }); - for modified_file in modified_files { - if pred(modified_file) { - return true; + let modified_files: Vec<_> = output + .lines() + .filter_map(|ln| { + let (status, name) = ln + .trim_end() + .split_once('\t') + .expect("bad format from `git diff --name-status`"); + if status == "M" { Some(name) } else { None } + }) + .collect(); + items.retain(|item| { + for modified_file in &modified_files { + if pred(item, modified_file) { + // at least one predicate matches, keep this item. + return true; + } } - } - false + // no predicates matched, remove this item. + false + }); } None => { eprintln!("warning: failed to run `git diff` to check for changes"); eprintln!("warning: assuming all files are modified"); - true } } } +/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files. +pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool { + let mut v = vec![()]; + files_modified_batch_filter(ci_info, &mut v, |_, p| pred(p)); + !v.is_empty() +} + pub mod alphabetical; pub mod bins; pub mod debug_artifacts; From 4096582662d38ca5d1692e0213ddf5574a40e0d6 Mon Sep 17 00:00:00 2001 From: binarycat Date: Mon, 28 Jul 2025 13:12:51 -0500 Subject: [PATCH 150/201] bootstrap: enable tidy auto extra checks on tools profile --- bootstrap.example.toml | 3 +++ src/bootstrap/defaults/bootstrap.tools.toml | 2 ++ 2 files changed, 5 insertions(+) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 73e93ccbe420..ef49113b70f4 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -475,6 +475,9 @@ # Note that if any value is manually given to bootstrap such as # `./x test tidy --extra-checks=js`, this value is ignored. # Use `--extra-checks=''` to temporarily disable all extra checks. +# +# Automatically enabled in the "tools" profile. +# Set to the empty string to force disable (recommeded for hdd systems). #build.tidy-extra-checks = "" # Indicates whether ccache is used when building certain artifacts (e.g. LLVM). diff --git a/src/bootstrap/defaults/bootstrap.tools.toml b/src/bootstrap/defaults/bootstrap.tools.toml index 57c2706f60a5..5abe636bd968 100644 --- a/src/bootstrap/defaults/bootstrap.tools.toml +++ b/src/bootstrap/defaults/bootstrap.tools.toml @@ -14,6 +14,8 @@ test-stage = 2 doc-stage = 2 # Contributors working on tools will probably expect compiler docs to be generated, so they can figure out how to use the API. compiler-docs = true +# Contributors working on tools are the most likely to change non-rust programs. +tidy-extra-checks = "auto:js,auto:py,auto:cpp,auto:spellcheck" [llvm] # Will download LLVM from CI if available on your platform. From 327ee15959949c808da2498f7337a5fa5e415c08 Mon Sep 17 00:00:00 2001 From: Noratrieb <48135649+Noratrieb@users.noreply.github.com> Date: Mon, 28 Jul 2025 20:47:01 +0200 Subject: [PATCH 151/201] Ensure external paths passed via flags end up in rustdoc depinfo rustdoc has many flags to pass external HTML/Markdown/CSS files that end up in the build. These need to be recorded in depinfo so that Cargo will rebuild the crate if they change. --- src/librustdoc/config.rs | 20 +++++++++++------- src/librustdoc/externalfiles.rs | 23 ++++++++++++++------- src/librustdoc/lib.rs | 8 ++++++- src/librustdoc/scrape_examples.rs | 2 ++ tests/run-make/rustdoc-dep-info/after.md | 1 + tests/run-make/rustdoc-dep-info/before.html | 0 tests/run-make/rustdoc-dep-info/extend.css | 0 tests/run-make/rustdoc-dep-info/rmake.rs | 15 +++++++++++++- tests/run-make/rustdoc-dep-info/theme.css | 1 + 9 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 tests/run-make/rustdoc-dep-info/after.md create mode 100644 tests/run-make/rustdoc-dep-info/before.html create mode 100644 tests/run-make/rustdoc-dep-info/extend.css create mode 100644 tests/run-make/rustdoc-dep-info/theme.css diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 986390dbaa08..dfa6bf175d94 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -377,7 +377,7 @@ impl Options { early_dcx: &mut EarlyDiagCtxt, matches: &getopts::Matches, args: Vec, - ) -> Option<(InputMode, Options, RenderOptions)> { + ) -> Option<(InputMode, Options, RenderOptions, Vec)> { // Check for unstable options. nightly_options::check_nightly_options(early_dcx, matches, &opts()); @@ -640,10 +640,13 @@ impl Options { let extension_css = matches.opt_str("e").map(|s| PathBuf::from(&s)); - if let Some(ref p) = extension_css - && !p.is_file() - { - dcx.fatal("option --extend-css argument must be a file"); + let mut loaded_paths = Vec::new(); + + if let Some(ref p) = extension_css { + loaded_paths.push(p.clone()); + if !p.is_file() { + dcx.fatal("option --extend-css argument must be a file"); + } } let mut themes = Vec::new(); @@ -687,6 +690,7 @@ impl Options { )) .emit(); } + loaded_paths.push(theme_file.clone()); themes.push(StylePath { path: theme_file }); } } @@ -705,6 +709,7 @@ impl Options { &mut id_map, edition, &None, + &mut loaded_paths, ) else { dcx.fatal("`ExternalHtml::load` failed"); }; @@ -796,7 +801,8 @@ impl Options { let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx); let with_examples = matches.opt_strs("with-examples"); - let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx); + let call_locations = + crate::scrape_examples::load_call_locations(with_examples, dcx, &mut loaded_paths); let doctest_build_args = matches.opt_strs("doctest-build-arg"); let unstable_features = @@ -881,7 +887,7 @@ impl Options { parts_out_dir, disable_minification, }; - Some((input, options, render_options)) + Some((input, options, render_options, loaded_paths)) } } diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs index ea2aa963eddf..42ade5b90048 100644 --- a/src/librustdoc/externalfiles.rs +++ b/src/librustdoc/externalfiles.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use std::{fs, str}; use rustc_errors::DiagCtxtHandle; @@ -32,12 +32,13 @@ impl ExternalHtml { id_map: &mut IdMap, edition: Edition, playground: &Option, + loaded_paths: &mut Vec, ) -> Option { let codes = ErrorCodes::from(nightly_build); - let ih = load_external_files(in_header, dcx)?; + let ih = load_external_files(in_header, dcx, loaded_paths)?; let bc = { - let mut bc = load_external_files(before_content, dcx)?; - let m_bc = load_external_files(md_before_content, dcx)?; + let mut bc = load_external_files(before_content, dcx, loaded_paths)?; + let m_bc = load_external_files(md_before_content, dcx, loaded_paths)?; Markdown { content: &m_bc, links: &[], @@ -52,8 +53,8 @@ impl ExternalHtml { bc }; let ac = { - let mut ac = load_external_files(after_content, dcx)?; - let m_ac = load_external_files(md_after_content, dcx)?; + let mut ac = load_external_files(after_content, dcx, loaded_paths)?; + let m_ac = load_external_files(md_after_content, dcx, loaded_paths)?; Markdown { content: &m_ac, links: &[], @@ -79,8 +80,10 @@ pub(crate) enum LoadStringError { pub(crate) fn load_string>( file_path: P, dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, ) -> Result { let file_path = file_path.as_ref(); + loaded_paths.push(file_path.to_owned()); let contents = match fs::read(file_path) { Ok(bytes) => bytes, Err(e) => { @@ -101,10 +104,14 @@ pub(crate) fn load_string>( } } -fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option { +fn load_external_files( + names: &[String], + dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, +) -> Option { let mut out = String::new(); for name in names { - let Ok(s) = load_string(name, dcx) else { return None }; + let Ok(s) = load_string(name, dcx, loaded_paths) else { return None }; out.push_str(&s); out.push('\n'); } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a3cdc4f687f2..7431ff8e1ade 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -799,7 +799,7 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { // Note that we discard any distinction between different non-zero exit // codes from `from_matches` here. - let (input, options, render_options) = + let (input, options, render_options, loaded_paths) = match config::Options::from_matches(early_dcx, &matches, args) { Some(opts) => opts, None => return, @@ -870,6 +870,12 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { interface::run_compiler(config, |compiler| { let sess = &compiler.sess; + // Register the loaded external files in the source map so they show up in depinfo. + // We can't load them via the source map because it gets created after we process the options. + for external_path in &loaded_paths { + let _ = sess.source_map().load_file(external_path); + } + if sess.opts.describe_lints { rustc_driver::describe_lints(sess, registered_lints); return; diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs index fceacb69fb55..4d29c74e1eb5 100644 --- a/src/librustdoc/scrape_examples.rs +++ b/src/librustdoc/scrape_examples.rs @@ -333,9 +333,11 @@ pub(crate) fn run( pub(crate) fn load_call_locations( with_examples: Vec, dcx: DiagCtxtHandle<'_>, + loaded_paths: &mut Vec, ) -> AllCallLocations { let mut all_calls: AllCallLocations = FxIndexMap::default(); for path in with_examples { + loaded_paths.push(path.clone().into()); let bytes = match fs::read(&path) { Ok(bytes) => bytes, Err(e) => dcx.fatal(format!("failed to load examples: {e}")), diff --git a/tests/run-make/rustdoc-dep-info/after.md b/tests/run-make/rustdoc-dep-info/after.md new file mode 100644 index 000000000000..10d4b4c41164 --- /dev/null +++ b/tests/run-make/rustdoc-dep-info/after.md @@ -0,0 +1 @@ +meow! :3 diff --git a/tests/run-make/rustdoc-dep-info/before.html b/tests/run-make/rustdoc-dep-info/before.html new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/run-make/rustdoc-dep-info/extend.css b/tests/run-make/rustdoc-dep-info/extend.css new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/run-make/rustdoc-dep-info/rmake.rs b/tests/run-make/rustdoc-dep-info/rmake.rs index db7a00a5ce21..625f81fd428e 100644 --- a/tests/run-make/rustdoc-dep-info/rmake.rs +++ b/tests/run-make/rustdoc-dep-info/rmake.rs @@ -9,13 +9,26 @@ use run_make_support::{path, rfs, rustdoc}; fn main() { // We're only emitting dep info, so we shouldn't be running static analysis to // figure out that this program is erroneous. - rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info").run(); + // Ensure that all kinds of input reading flags end up in dep-info. + rustdoc() + .input("lib.rs") + .arg("-Zunstable-options") + .arg("--html-before-content=before.html") + .arg("--markdown-after-content=after.md") + .arg("--extend-css=extend.css") + .arg("--theme=theme.css") + .emit("dep-info") + .run(); let content = rfs::read_to_string("foo.d"); assert_contains(&content, "lib.rs:"); assert_contains(&content, "foo.rs:"); assert_contains(&content, "bar.rs:"); assert_contains(&content, "doc.md:"); + assert_contains(&content, "after.md:"); + assert_contains(&content, "before.html:"); + assert_contains(&content, "extend.css:"); + assert_contains(&content, "theme.css:"); // Now we check that we can provide a file name to the `dep-info` argument. rustdoc().input("lib.rs").arg("-Zunstable-options").emit("dep-info=bla.d").run(); diff --git a/tests/run-make/rustdoc-dep-info/theme.css b/tests/run-make/rustdoc-dep-info/theme.css new file mode 100644 index 000000000000..459daffa9a19 --- /dev/null +++ b/tests/run-make/rustdoc-dep-info/theme.css @@ -0,0 +1 @@ +/* This is not a valid theme but that doesn't really matter */ From aa9767290e46c9c73fa1fd3abb52277b4d07cfae Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 30 May 2025 16:03:32 -0600 Subject: [PATCH 152/201] feat: Right align line numbers --- compiler/rustc_errors/src/emitter.rs | 50 +++++++++++-------- compiler/rustc_parse/src/parser/tests.rs | 48 +++++++++--------- .../foo.stderr | 12 ++--- .../doctest/standalone-warning-2024.stderr | 2 +- .../ui-testing-optout.stderr | 2 +- tests/ui/modules/issue-107649.stderr | 4 +- 6 files changed, 64 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 95400ac2ca3b..46a4a1868247 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -713,8 +713,7 @@ impl HumanEmitter { Style::LineNumber, ); } - buffer.puts(line_offset, 0, &self.maybe_anonymized(line_index), Style::LineNumber); - + self.draw_line_num(buffer, line_index, line_offset, width_offset - 3); self.draw_col_separator_no_space(buffer, line_offset, width_offset - 2); left } @@ -2128,11 +2127,11 @@ impl HumanEmitter { // Account for a suggestion to completely remove a line(s) with whitespace (#94192). let line_end = sm.lookup_char_pos(parts[0].span.hi()).line; for line in line_start..=line_end { - buffer.puts( + self.draw_line_num( + &mut buffer, + line, row_num - 1 + line - line_start, - 0, - &self.maybe_anonymized(line), - Style::LineNumber, + max_line_num_len, ); buffer.puts( row_num - 1 + line - line_start, @@ -2612,12 +2611,7 @@ impl HumanEmitter { // For more info: https://github.com/rust-lang/rust/issues/92741 let lines_to_remove = file_lines.lines.iter().take(file_lines.lines.len() - 1); for (index, line_to_remove) in lines_to_remove.enumerate() { - buffer.puts( - *row_num - 1, - 0, - &self.maybe_anonymized(line_num + index), - Style::LineNumber, - ); + self.draw_line_num(buffer, line_num + index, *row_num - 1, max_line_num_len); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); let line = normalize_whitespace( &file_lines.file.get_line(line_to_remove.line_index).unwrap(), @@ -2634,11 +2628,11 @@ impl HumanEmitter { let last_line_index = file_lines.lines[file_lines.lines.len() - 1].line_index; let last_line = &file_lines.file.get_line(last_line_index).unwrap(); if last_line != line_to_add { - buffer.puts( + self.draw_line_num( + buffer, + line_num + file_lines.lines.len() - 1, *row_num - 1, - 0, - &self.maybe_anonymized(line_num + file_lines.lines.len() - 1), - Style::LineNumber, + max_line_num_len, ); buffer.puts(*row_num - 1, max_line_num_len + 1, "- ", Style::Removal); buffer.puts( @@ -2661,7 +2655,7 @@ impl HumanEmitter { // 2 - .await // | // *row_num -= 1; - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { @@ -2671,7 +2665,7 @@ impl HumanEmitter { *row_num -= 2; } } else if is_multiline { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); match &highlight_parts { [SubstitutionHighlight { start: 0, end }] if *end == line_to_add.len() => { buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); @@ -2702,11 +2696,11 @@ impl HumanEmitter { Style::NoStyle, ); } else if let DisplaySuggestion::Add = show_code_change { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } else { - buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber); + self.draw_line_num(buffer, line_num, *row_num, max_line_num_len); self.draw_col_separator(buffer, *row_num, max_line_num_len + 1); buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle); } @@ -3016,6 +3010,22 @@ impl HumanEmitter { OutputTheme::Unicode => "…", } } + + fn draw_line_num( + &self, + buffer: &mut StyledBuffer, + line_num: usize, + line_offset: usize, + max_line_num_len: usize, + ) { + let line_num = self.maybe_anonymized(line_num); + buffer.puts( + line_offset, + max_line_num_len.saturating_sub(str_width(&line_num)), + &line_num, + Style::LineNumber, + ); + } } #[derive(Debug, Clone, Copy)] diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index 15679d23bc56..43a1d779a75d 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -2114,15 +2114,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | X1 Y1 Z1 + 4 | | X1 Y1 Z1 | | ____^____- | ||____| | | `X` is a good letter -5 | | 1 -6 | | 2 -7 | | 3 + 5 | | 1 + 6 | | 2 + 7 | | 3 ... | 15 | | X2 Y2 Z2 16 | | X3 Y3 Z3 @@ -2133,15 +2133,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ X1 Y1 Z1 + 4 │ ┃ X1 Y1 Z1 │ ┃┌────╿────┘ │ ┗│━━━━┥ │ │ `X` is a good letter -5 │ │ 1 -6 │ │ 2 -7 │ │ 3 + 5 │ │ 1 + 6 │ │ 2 + 7 │ │ 3 ‡ │ 15 │ │ X2 Y2 Z2 16 │ │ X3 Y3 Z3 @@ -2189,15 +2189,15 @@ fn foo() { error: foo --> test.rs:3:6 | -3 | X0 Y0 Z0 + 3 | X0 Y0 Z0 | _______^ -4 | | 1 -5 | | 2 -6 | | 3 -7 | | X1 Y1 Z1 + 4 | | 1 + 5 | | 2 + 6 | | 3 + 7 | | X1 Y1 Z1 | | _________- -8 | || 4 -9 | || 5 + 8 | || 4 + 9 | || 5 10 | || 6 11 | || X2 Y2 Z2 | ||__________- `Z` is a good letter too @@ -2211,15 +2211,15 @@ error: foo error: foo ╭▸ test.rs:3:6 │ -3 │ X0 Y0 Z0 + 3 │ X0 Y0 Z0 │ ┏━━━━━━━┛ -4 │ ┃ 1 -5 │ ┃ 2 -6 │ ┃ 3 -7 │ ┃ X1 Y1 Z1 + 4 │ ┃ 1 + 5 │ ┃ 2 + 6 │ ┃ 3 + 7 │ ┃ X1 Y1 Z1 │ ┃┌─────────┘ -8 │ ┃│ 4 -9 │ ┃│ 5 + 8 │ ┃│ 4 + 9 │ ┃│ 5 10 │ ┃│ 6 11 │ ┃│ X2 Y2 Z2 │ ┃└──────────┘ `Z` is a good letter too diff --git a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr index 7f131153540b..9433a0ba00e8 100644 --- a/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr +++ b/tests/run-make/crate-loading-crate-depends-on-itself/foo.stderr @@ -7,19 +7,19 @@ error[E0277]: the trait bound `foo::Struct: Trait` is not satisfied note: there are multiple different versions of crate `foo` in the dependency graph --> foo-current.rs:7:1 | -4 | extern crate foo; + 4 | extern crate foo; | ----------------- one version of crate `foo` used here, as a direct dependency of the current crate -5 | -6 | pub struct Struct; + 5 | + 6 | pub struct Struct; | ----------------- this type implements the required trait -7 | pub trait Trait {} + 7 | pub trait Trait {} | ^^^^^^^^^^^^^^^ this is the required trait | ::: foo-prev.rs:X:Y | -4 | pub struct Struct; + 4 | pub struct Struct; | ----------------- this type doesn't implement the required trait -5 | pub trait Trait {} + 5 | pub trait Trait {} | --------------- this is the found trait = note: two types coming from two different versions of the same crate are different types even if they look the same = help: you can use `cargo tree` to explore your dependency tree diff --git a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr index bfc1e9194045..ce65557c2c4a 100644 --- a/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr +++ b/tests/rustdoc-ui/doctest/standalone-warning-2024.stderr @@ -15,7 +15,7 @@ error: unknown attribute `standalone` note: the lint level is defined here --> $DIR/standalone-warning-2024.rs:9:9 | -9 | #![deny(warnings)] + 9 | #![deny(warnings)] | ^^^^^^^^ = note: `#[deny(rustdoc::invalid_codeblock_attributes)]` implied by `#[deny(warnings)]` diff --git a/tests/ui/compiletest-self-test/ui-testing-optout.stderr b/tests/ui/compiletest-self-test/ui-testing-optout.stderr index 652c472c0bcf..f1d03eab14a9 100644 --- a/tests/ui/compiletest-self-test/ui-testing-optout.stderr +++ b/tests/ui/compiletest-self-test/ui-testing-optout.stderr @@ -16,7 +16,7 @@ error[E0412]: cannot find type `D` in this scope error[E0412]: cannot find type `F` in this scope --> $DIR/ui-testing-optout.rs:92:10 | -4 | type A = B; + 4 | type A = B; | ----------- similarly named type alias `A` defined here ... 92 | type E = F; diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr index 802ac669a10e..49d7cb4e0aad 100644 --- a/tests/ui/modules/issue-107649.stderr +++ b/tests/ui/modules/issue-107649.stderr @@ -9,8 +9,8 @@ error[E0277]: `Dummy` doesn't implement `Debug` help: consider annotating `Dummy` with `#[derive(Debug)]` --> $DIR/auxiliary/dummy_lib.rs:2:1 | -2 + #[derive(Debug)] -3 | pub struct Dummy; + 2 + #[derive(Debug)] + 3 | pub struct Dummy; | error: aborting due to 1 previous error From be9d3bcfed2df9926f620af84bde1d687a9f6962 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Fri, 4 Jul 2025 19:13:24 +0000 Subject: [PATCH 153/201] Make resolve_fn_signature responsible for its own rib. --- compiler/rustc_resolve/src/late.rs | 129 ++++++++++++----------------- 1 file changed, 54 insertions(+), 75 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index ed9622a0d81c..261d099abdc0 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -910,22 +910,15 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc span, |this| { this.visit_generic_params(&fn_ptr.generic_params, false); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: ty.id, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - ty.id, - false, - // We don't need to deal with patterns in parameters, because - // they are not possible for foreign or bodiless functions. - fn_ptr.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &fn_ptr.decl.output, - ) - }, - ); + this.resolve_fn_signature( + ty.id, + false, + // We don't need to deal with patterns in parameters, because + // they are not possible for foreign or bodiless functions. + fn_ptr.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &fn_ptr.decl.output, + false, + ) }, ) } @@ -1042,19 +1035,12 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc self.visit_fn_header(&sig.header); self.visit_ident(ident); self.visit_generics(generics); - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - fn_id, - sig.decl.has_self(), - sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), - &sig.decl.output, - ); - }, + self.resolve_fn_signature( + fn_id, + sig.decl.has_self(), + sig.decl.inputs.iter().map(|Param { ty, .. }| (None, &**ty)), + &sig.decl.output, + false, ); return; } @@ -1080,22 +1066,15 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc .coroutine_kind .map(|coroutine_kind| coroutine_kind.return_id()); - this.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder: fn_id, - report_in_path: coro_node_id.is_some(), - }, - |this| { - this.resolve_fn_signature( - fn_id, - declaration.has_self(), - declaration - .inputs - .iter() - .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), - &declaration.output, - ); - }, + this.resolve_fn_signature( + fn_id, + declaration.has_self(), + declaration + .inputs + .iter() + .map(|Param { pat, ty, .. }| (Some(&**pat), &**ty)), + &declaration.output, + coro_node_id.is_some(), ); if let Some(contract) = contract { @@ -1307,19 +1286,12 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc kind: LifetimeBinderKind::PolyTrait, .. } => { - self.with_lifetime_rib( - LifetimeRibKind::AnonymousCreateParameter { - binder, - report_in_path: false, - }, - |this| { - this.resolve_fn_signature( - binder, - false, - p_args.inputs.iter().map(|ty| (None, &**ty)), - &p_args.output, - ) - }, + self.resolve_fn_signature( + binder, + false, + p_args.inputs.iter().map(|ty| (None, &**ty)), + &p_args.output, + false, ); break; } @@ -2236,25 +2208,32 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { has_self: bool, inputs: impl Iterator, &'ast Ty)> + Clone, output_ty: &'ast FnRetTy, + report_elided_lifetimes_in_path: bool, ) { - // Add each argument to the rib. - let elision_lifetime = self.resolve_fn_params(has_self, inputs); - debug!(?elision_lifetime); - - let outer_failures = take(&mut self.diag_metadata.current_elision_failures); - let output_rib = if let Ok(res) = elision_lifetime.as_ref() { - self.r.lifetime_elision_allowed.insert(fn_id); - LifetimeRibKind::Elided(*res) - } else { - LifetimeRibKind::ElisionFailure + let rib = LifetimeRibKind::AnonymousCreateParameter { + binder: fn_id, + report_in_path: report_elided_lifetimes_in_path, }; - self.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, output_ty)); - let elision_failures = - replace(&mut self.diag_metadata.current_elision_failures, outer_failures); - if !elision_failures.is_empty() { - let Err(failure_info) = elision_lifetime else { bug!() }; - self.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); - } + self.with_lifetime_rib(rib, |this| { + // Add each argument to the rib. + let elision_lifetime = this.resolve_fn_params(has_self, inputs); + debug!(?elision_lifetime); + + let outer_failures = take(&mut this.diag_metadata.current_elision_failures); + let output_rib = if let Ok(res) = elision_lifetime.as_ref() { + this.r.lifetime_elision_allowed.insert(fn_id); + LifetimeRibKind::Elided(*res) + } else { + LifetimeRibKind::ElisionFailure + }; + this.with_lifetime_rib(output_rib, |this| visit::walk_fn_ret_ty(this, output_ty)); + let elision_failures = + replace(&mut this.diag_metadata.current_elision_failures, outer_failures); + if !elision_failures.is_empty() { + let Err(failure_info) = elision_lifetime else { bug!() }; + this.report_missing_lifetime_specifiers(elision_failures, Some(failure_info)); + } + }); } /// Resolve inside function parameters and parameter types. From 7ca4d1f6a155e802b31cfc1f0971f3916aa8e02d Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 29 Jul 2025 10:29:32 +1000 Subject: [PATCH 154/201] coverage: Regression test for "function name is empty" bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bug was triggered by a particular usage of the `?` try operator in a proc-macro expansion. Thanks to lqd for the minimization. Co-authored-by: Rémy Rakic --- .../coverage/auxiliary/try_in_macro_helper.rs | 31 +++++++++++++ tests/coverage/try-in-macro.attr.cov-map | 20 +++++++++ tests/coverage/try-in-macro.attr.coverage | 44 +++++++++++++++++++ tests/coverage/try-in-macro.bang.cov-map | 20 +++++++++ tests/coverage/try-in-macro.bang.coverage | 44 +++++++++++++++++++ tests/coverage/try-in-macro.derive.cov-map | 20 +++++++++ tests/coverage/try-in-macro.derive.coverage | 44 +++++++++++++++++++ tests/coverage/try-in-macro.rs | 43 ++++++++++++++++++ 8 files changed, 266 insertions(+) create mode 100644 tests/coverage/auxiliary/try_in_macro_helper.rs create mode 100644 tests/coverage/try-in-macro.attr.cov-map create mode 100644 tests/coverage/try-in-macro.attr.coverage create mode 100644 tests/coverage/try-in-macro.bang.cov-map create mode 100644 tests/coverage/try-in-macro.bang.coverage create mode 100644 tests/coverage/try-in-macro.derive.cov-map create mode 100644 tests/coverage/try-in-macro.derive.coverage create mode 100644 tests/coverage/try-in-macro.rs diff --git a/tests/coverage/auxiliary/try_in_macro_helper.rs b/tests/coverage/auxiliary/try_in_macro_helper.rs new file mode 100644 index 000000000000..27d2af15b055 --- /dev/null +++ b/tests/coverage/auxiliary/try_in_macro_helper.rs @@ -0,0 +1,31 @@ +//@ edition: 2024 +// (The proc-macro crate doesn't need to be instrumented.) +//@ compile-flags: -Cinstrument-coverage=off + +use proc_macro::TokenStream; + +/// Minimized form of `#[derive(arbitrary::Arbitrary)]` that still triggers +/// the original bug. +const CODE: &str = " + impl Arbitrary for MyEnum { + fn try_size_hint() -> Option { + Some(0)?; + None + } + } +"; + +#[proc_macro_attribute] +pub fn attr(_attr: TokenStream, _item: TokenStream) -> TokenStream { + CODE.parse().unwrap() +} + +#[proc_macro] +pub fn bang(_item: TokenStream) -> TokenStream { + CODE.parse().unwrap() +} + +#[proc_macro_derive(Arbitrary)] +pub fn derive_arbitrary(_item: TokenStream) -> TokenStream { + CODE.parse().unwrap() +} diff --git a/tests/coverage/try-in-macro.attr.cov-map b/tests/coverage/try-in-macro.attr.cov-map new file mode 100644 index 000000000000..7111e89637ce --- /dev/null +++ b/tests/coverage/try-in-macro.attr.cov-map @@ -0,0 +1,20 @@ +Function name: ::try_size_hint +Raw bytes (9): 0x[01, 01, 00, 01, 00, 1e, 2a, 00, 2b] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 30, 42) to (start + 0, 43) +Highest counter ID seen: (none) + +Function name: try_in_macro::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 29, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 41, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/try-in-macro.attr.coverage b/tests/coverage/try-in-macro.attr.coverage new file mode 100644 index 000000000000..457a161f3c79 --- /dev/null +++ b/tests/coverage/try-in-macro.attr.coverage @@ -0,0 +1,44 @@ + LL| |//! Regression test for . + LL| |//! + LL| |//! The changes in exposed a + LL| |//! latent bug that would sometimes cause the compiler to emit a covfun record + LL| |//! for a function, but not emit a corresponding PGO symbol name entry, because + LL| |//! the function did not have any physical coverage counters. The `llvm-cov` + LL| |//! tool would then fail to resolve the covfun record's function name hash, + LL| |//! and exit with the cryptic error: + LL| |//! + LL| |//! ```text + LL| |//! malformed instrumentation profile data: function name is empty + LL| |//! ``` + LL| |//! + LL| |//! The bug was then triggered in the wild by the macro-expansion of + LL| |//! `#[derive(arbitrary::Arbitrary)]`. + LL| |//! + LL| |//! This test uses a minimized form of the `Arbitrary` derive macro that was + LL| |//! found to still trigger the original bug. The bug could also be triggered + LL| |//! by a bang proc-macro or an attribute proc-macro. + LL| | + LL| |//@ edition: 2024 + LL| |//@ revisions: attr bang derive + LL| |//@ proc-macro: try_in_macro_helper.rs + LL| | + LL| |trait Arbitrary { + LL| | fn try_size_hint() -> Option; + LL| |} + LL| | + LL| |// Expand via an attribute proc-macro. + LL| |#[cfg_attr(attr, try_in_macro_helper::attr)] + LL| |const _: () = (); + LL| | + LL| |// Expand via a regular bang-style proc-macro. + LL| |#[cfg(bang)] + LL| |try_in_macro_helper::bang!(); + LL| | + LL| |// Expand via a derive proc-macro. + LL| |#[cfg_attr(derive, derive(try_in_macro_helper::Arbitrary))] + LL| |enum MyEnum {} + LL| | + LL| 1|fn main() { + LL| 1| MyEnum::try_size_hint(); + LL| 1|} + diff --git a/tests/coverage/try-in-macro.bang.cov-map b/tests/coverage/try-in-macro.bang.cov-map new file mode 100644 index 000000000000..80bd91a993c0 --- /dev/null +++ b/tests/coverage/try-in-macro.bang.cov-map @@ -0,0 +1,20 @@ +Function name: ::try_size_hint +Raw bytes (9): 0x[01, 01, 00, 01, 00, 23, 1c, 00, 1d] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 35, 28) to (start + 0, 29) +Highest counter ID seen: (none) + +Function name: try_in_macro::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 29, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 41, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/try-in-macro.bang.coverage b/tests/coverage/try-in-macro.bang.coverage new file mode 100644 index 000000000000..457a161f3c79 --- /dev/null +++ b/tests/coverage/try-in-macro.bang.coverage @@ -0,0 +1,44 @@ + LL| |//! Regression test for . + LL| |//! + LL| |//! The changes in exposed a + LL| |//! latent bug that would sometimes cause the compiler to emit a covfun record + LL| |//! for a function, but not emit a corresponding PGO symbol name entry, because + LL| |//! the function did not have any physical coverage counters. The `llvm-cov` + LL| |//! tool would then fail to resolve the covfun record's function name hash, + LL| |//! and exit with the cryptic error: + LL| |//! + LL| |//! ```text + LL| |//! malformed instrumentation profile data: function name is empty + LL| |//! ``` + LL| |//! + LL| |//! The bug was then triggered in the wild by the macro-expansion of + LL| |//! `#[derive(arbitrary::Arbitrary)]`. + LL| |//! + LL| |//! This test uses a minimized form of the `Arbitrary` derive macro that was + LL| |//! found to still trigger the original bug. The bug could also be triggered + LL| |//! by a bang proc-macro or an attribute proc-macro. + LL| | + LL| |//@ edition: 2024 + LL| |//@ revisions: attr bang derive + LL| |//@ proc-macro: try_in_macro_helper.rs + LL| | + LL| |trait Arbitrary { + LL| | fn try_size_hint() -> Option; + LL| |} + LL| | + LL| |// Expand via an attribute proc-macro. + LL| |#[cfg_attr(attr, try_in_macro_helper::attr)] + LL| |const _: () = (); + LL| | + LL| |// Expand via a regular bang-style proc-macro. + LL| |#[cfg(bang)] + LL| |try_in_macro_helper::bang!(); + LL| | + LL| |// Expand via a derive proc-macro. + LL| |#[cfg_attr(derive, derive(try_in_macro_helper::Arbitrary))] + LL| |enum MyEnum {} + LL| | + LL| 1|fn main() { + LL| 1| MyEnum::try_size_hint(); + LL| 1|} + diff --git a/tests/coverage/try-in-macro.derive.cov-map b/tests/coverage/try-in-macro.derive.cov-map new file mode 100644 index 000000000000..6646b6693bae --- /dev/null +++ b/tests/coverage/try-in-macro.derive.cov-map @@ -0,0 +1,20 @@ +Function name: ::try_size_hint +Raw bytes (9): 0x[01, 01, 00, 01, 00, 26, 38, 00, 39] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 1 +- Code(Zero) at (prev + 38, 56) to (start + 0, 57) +Highest counter ID seen: (none) + +Function name: try_in_macro::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 29, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/try-in-macro.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 41, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/try-in-macro.derive.coverage b/tests/coverage/try-in-macro.derive.coverage new file mode 100644 index 000000000000..457a161f3c79 --- /dev/null +++ b/tests/coverage/try-in-macro.derive.coverage @@ -0,0 +1,44 @@ + LL| |//! Regression test for . + LL| |//! + LL| |//! The changes in exposed a + LL| |//! latent bug that would sometimes cause the compiler to emit a covfun record + LL| |//! for a function, but not emit a corresponding PGO symbol name entry, because + LL| |//! the function did not have any physical coverage counters. The `llvm-cov` + LL| |//! tool would then fail to resolve the covfun record's function name hash, + LL| |//! and exit with the cryptic error: + LL| |//! + LL| |//! ```text + LL| |//! malformed instrumentation profile data: function name is empty + LL| |//! ``` + LL| |//! + LL| |//! The bug was then triggered in the wild by the macro-expansion of + LL| |//! `#[derive(arbitrary::Arbitrary)]`. + LL| |//! + LL| |//! This test uses a minimized form of the `Arbitrary` derive macro that was + LL| |//! found to still trigger the original bug. The bug could also be triggered + LL| |//! by a bang proc-macro or an attribute proc-macro. + LL| | + LL| |//@ edition: 2024 + LL| |//@ revisions: attr bang derive + LL| |//@ proc-macro: try_in_macro_helper.rs + LL| | + LL| |trait Arbitrary { + LL| | fn try_size_hint() -> Option; + LL| |} + LL| | + LL| |// Expand via an attribute proc-macro. + LL| |#[cfg_attr(attr, try_in_macro_helper::attr)] + LL| |const _: () = (); + LL| | + LL| |// Expand via a regular bang-style proc-macro. + LL| |#[cfg(bang)] + LL| |try_in_macro_helper::bang!(); + LL| | + LL| |// Expand via a derive proc-macro. + LL| |#[cfg_attr(derive, derive(try_in_macro_helper::Arbitrary))] + LL| |enum MyEnum {} + LL| | + LL| 1|fn main() { + LL| 1| MyEnum::try_size_hint(); + LL| 1|} + diff --git a/tests/coverage/try-in-macro.rs b/tests/coverage/try-in-macro.rs new file mode 100644 index 000000000000..ab9d66754180 --- /dev/null +++ b/tests/coverage/try-in-macro.rs @@ -0,0 +1,43 @@ +//! Regression test for . +//! +//! The changes in exposed a +//! latent bug that would sometimes cause the compiler to emit a covfun record +//! for a function, but not emit a corresponding PGO symbol name entry, because +//! the function did not have any physical coverage counters. The `llvm-cov` +//! tool would then fail to resolve the covfun record's function name hash, +//! and exit with the cryptic error: +//! +//! ```text +//! malformed instrumentation profile data: function name is empty +//! ``` +//! +//! The bug was then triggered in the wild by the macro-expansion of +//! `#[derive(arbitrary::Arbitrary)]`. +//! +//! This test uses a minimized form of the `Arbitrary` derive macro that was +//! found to still trigger the original bug. The bug could also be triggered +//! by a bang proc-macro or an attribute proc-macro. + +//@ edition: 2024 +//@ revisions: attr bang derive +//@ proc-macro: try_in_macro_helper.rs + +trait Arbitrary { + fn try_size_hint() -> Option; +} + +// Expand via an attribute proc-macro. +#[cfg_attr(attr, try_in_macro_helper::attr)] +const _: () = (); + +// Expand via a regular bang-style proc-macro. +#[cfg(bang)] +try_in_macro_helper::bang!(); + +// Expand via a derive proc-macro. +#[cfg_attr(derive, derive(try_in_macro_helper::Arbitrary))] +enum MyEnum {} + +fn main() { + MyEnum::try_size_hint(); +} From dc33eb6c424a1325e53644517f78c3461591211d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 08:10:55 +0200 Subject: [PATCH 155/201] update lockfile and bless tidy --- Cargo.lock | 139 +++++++++++++++++++++ src/bootstrap/src/utils/proc_macro_deps.rs | 5 + 2 files changed, 144 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 6be3bced5e16..4be66fe25d3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,6 +466,8 @@ version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -655,6 +657,26 @@ dependencies = [ "serde", ] +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "codespan-reporting" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +dependencies = [ + "serde", + "termcolor", + "unicode-width 0.2.1", +] + [[package]] name = "collect-license-metadata" version = "0.1.0" @@ -913,6 +935,68 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "cxx" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +dependencies = [ + "cc", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.104", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.104", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.161" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.104", +] + [[package]] name = "darling" version = "0.20.11" @@ -1373,6 +1457,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "genmc-sys" +version = "0.1.0" +dependencies = [ + "cc", + "cmake", + "cxx", + "cxx-build", + "git2", +] + [[package]] name = "getopts" version = "0.2.23" @@ -1427,6 +1522,21 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "git2" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110" +dependencies = [ + "bitflags", + "libc", + "libgit2-sys", + "log", + "openssl-probe", + "openssl-sys", + "url", +] + [[package]] name = "glob" version = "0.3.2" @@ -2060,6 +2170,19 @@ dependencies = [ "cc", ] +[[package]] +name = "libgit2-sys" +version = "0.18.2+1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", +] + [[package]] name = "libloading" version = "0.8.8" @@ -2099,6 +2222,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "link-cplusplus" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6f6da007f968f9def0d65a05b187e2960183de70c160204ecfccf0ee330212" +dependencies = [ + "cc", +] + [[package]] name = "linkchecker" version = "0.1.0" @@ -2308,6 +2440,7 @@ dependencies = [ "chrono-tz", "colored 3.0.0", "directories", + "genmc-sys", "getrandom 0.3.3", "ipc-channel", "libc", @@ -4877,6 +5010,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "scratch" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" + [[package]] name = "self_cell" version = "1.2.0" diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index 21c7fc89d7df..777c8601aa1d 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -3,6 +3,7 @@ /// See pub static CRATES: &[&str] = &[ // tidy-alphabetical-start + "allocator-api2", "annotate-snippets", "anstyle", "askama_parser", @@ -16,13 +17,17 @@ pub static CRATES: &[&str] = &[ "darling_core", "derive_builder_core", "digest", + "equivalent", "fluent-bundle", "fluent-langneg", "fluent-syntax", "fnv", + "foldhash", "generic-array", + "hashbrown", "heck", "ident_case", + "indexmap", "intl-memoizer", "intl_pluralrules", "libc", From 950a3b34ea25631749708029594f892df4399c7d Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Mon, 28 Jul 2025 01:12:05 -0700 Subject: [PATCH 156/201] Add a mir-opt pre-codegen test for dropping a `Box<[impl Copy]>` --- ...ace.PreCodegen.after.32bit.panic-abort.mir | 111 ++++++++++++++++++ ...ce.PreCodegen.after.32bit.panic-unwind.mir | 111 ++++++++++++++++++ ...ace.PreCodegen.after.64bit.panic-abort.mir | 111 ++++++++++++++++++ ...ce.PreCodegen.after.64bit.panic-unwind.mir | 111 ++++++++++++++++++ tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 19 +++ 5 files changed, 463 insertions(+) create mode 100644 tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir create mode 100644 tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir create mode 100644 tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir create mode 100644 tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir create mode 100644 tests/mir-opt/pre-codegen/drop_boxed_slice.rs diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir new file mode 100644 index 000000000000..587e69154ea1 --- /dev/null +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -0,0 +1,111 @@ +// MIR for `generic_in_place` after PreCodegen + +fn generic_in_place(_1: *mut Box<[T]>) -> () { + debug ptr => _1; + let mut _0: (); + scope 1 (inlined drop_in_place::> - shim(Some(Box<[T]>))) { + scope 2 (inlined as Drop>::drop) { + let _2: std::ptr::NonNull<[T]>; + let mut _3: *mut [T]; + let mut _4: *const [T]; + let _12: (); + scope 3 { + let _8: std::ptr::alignment::AlignmentEnum; + scope 4 { + scope 12 (inlined Layout::size) { + } + scope 13 (inlined Unique::<[T]>::cast::) { + scope 14 (inlined NonNull::<[T]>::cast::) { + scope 15 (inlined NonNull::<[T]>::as_ptr) { + } + } + } + scope 16 (inlined as From>>::from) { + scope 17 (inlined Unique::::as_non_null_ptr) { + } + } + scope 18 (inlined ::deallocate) { + let mut _9: *mut u8; + scope 19 (inlined Layout::size) { + } + scope 20 (inlined NonNull::::as_ptr) { + } + scope 21 (inlined std::alloc::dealloc) { + let mut _11: usize; + scope 22 (inlined Layout::size) { + } + scope 23 (inlined Layout::align) { + scope 24 (inlined std::ptr::Alignment::as_usize) { + let mut _10: u32; + } + } + } + } + } + scope 5 (inlined Unique::<[T]>::as_ptr) { + scope 6 (inlined NonNull::<[T]>::as_ptr) { + } + } + scope 7 (inlined Layout::for_value_raw::<[T]>) { + let mut _5: usize; + let mut _6: usize; + scope 8 { + scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { + let mut _7: std::ptr::Alignment; + } + } + scope 9 (inlined size_of_val_raw::<[T]>) { + } + scope 10 (inlined align_of_val_raw::<[T]>) { + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); + StorageLive(_4); + _3 = copy _2 as *mut [T] (Transmute); + _4 = copy _2 as *const [T] (Transmute); + StorageLive(_6); + _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageLive(_7); + _7 = copy _6 as std::ptr::Alignment (Transmute); + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); + StorageDead(_6); + StorageDead(_4); + switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + } + + bb3: { + StorageLive(_9); + _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_11); + StorageLive(_10); + _10 = discriminant(_8); + _11 = move _10 as usize (IntToInt); + StorageDead(_10); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_11); + StorageDead(_9); + goto -> bb5; + } + + bb5: { + StorageDead(_2); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir new file mode 100644 index 000000000000..587e69154ea1 --- /dev/null +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -0,0 +1,111 @@ +// MIR for `generic_in_place` after PreCodegen + +fn generic_in_place(_1: *mut Box<[T]>) -> () { + debug ptr => _1; + let mut _0: (); + scope 1 (inlined drop_in_place::> - shim(Some(Box<[T]>))) { + scope 2 (inlined as Drop>::drop) { + let _2: std::ptr::NonNull<[T]>; + let mut _3: *mut [T]; + let mut _4: *const [T]; + let _12: (); + scope 3 { + let _8: std::ptr::alignment::AlignmentEnum; + scope 4 { + scope 12 (inlined Layout::size) { + } + scope 13 (inlined Unique::<[T]>::cast::) { + scope 14 (inlined NonNull::<[T]>::cast::) { + scope 15 (inlined NonNull::<[T]>::as_ptr) { + } + } + } + scope 16 (inlined as From>>::from) { + scope 17 (inlined Unique::::as_non_null_ptr) { + } + } + scope 18 (inlined ::deallocate) { + let mut _9: *mut u8; + scope 19 (inlined Layout::size) { + } + scope 20 (inlined NonNull::::as_ptr) { + } + scope 21 (inlined std::alloc::dealloc) { + let mut _11: usize; + scope 22 (inlined Layout::size) { + } + scope 23 (inlined Layout::align) { + scope 24 (inlined std::ptr::Alignment::as_usize) { + let mut _10: u32; + } + } + } + } + } + scope 5 (inlined Unique::<[T]>::as_ptr) { + scope 6 (inlined NonNull::<[T]>::as_ptr) { + } + } + scope 7 (inlined Layout::for_value_raw::<[T]>) { + let mut _5: usize; + let mut _6: usize; + scope 8 { + scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { + let mut _7: std::ptr::Alignment; + } + } + scope 9 (inlined size_of_val_raw::<[T]>) { + } + scope 10 (inlined align_of_val_raw::<[T]>) { + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); + StorageLive(_4); + _3 = copy _2 as *mut [T] (Transmute); + _4 = copy _2 as *const [T] (Transmute); + StorageLive(_6); + _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageLive(_7); + _7 = copy _6 as std::ptr::Alignment (Transmute); + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); + StorageDead(_6); + StorageDead(_4); + switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + } + + bb3: { + StorageLive(_9); + _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_11); + StorageLive(_10); + _10 = discriminant(_8); + _11 = move _10 as usize (IntToInt); + StorageDead(_10); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_11); + StorageDead(_9); + goto -> bb5; + } + + bb5: { + StorageDead(_2); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir new file mode 100644 index 000000000000..e88f6b394fa7 --- /dev/null +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -0,0 +1,111 @@ +// MIR for `generic_in_place` after PreCodegen + +fn generic_in_place(_1: *mut Box<[T]>) -> () { + debug ptr => _1; + let mut _0: (); + scope 1 (inlined drop_in_place::> - shim(Some(Box<[T]>))) { + scope 2 (inlined as Drop>::drop) { + let _2: std::ptr::NonNull<[T]>; + let mut _3: *mut [T]; + let mut _4: *const [T]; + let _12: (); + scope 3 { + let _8: std::ptr::alignment::AlignmentEnum; + scope 4 { + scope 12 (inlined Layout::size) { + } + scope 13 (inlined Unique::<[T]>::cast::) { + scope 14 (inlined NonNull::<[T]>::cast::) { + scope 15 (inlined NonNull::<[T]>::as_ptr) { + } + } + } + scope 16 (inlined as From>>::from) { + scope 17 (inlined Unique::::as_non_null_ptr) { + } + } + scope 18 (inlined ::deallocate) { + let mut _9: *mut u8; + scope 19 (inlined Layout::size) { + } + scope 20 (inlined NonNull::::as_ptr) { + } + scope 21 (inlined std::alloc::dealloc) { + let mut _11: usize; + scope 22 (inlined Layout::size) { + } + scope 23 (inlined Layout::align) { + scope 24 (inlined std::ptr::Alignment::as_usize) { + let mut _10: u64; + } + } + } + } + } + scope 5 (inlined Unique::<[T]>::as_ptr) { + scope 6 (inlined NonNull::<[T]>::as_ptr) { + } + } + scope 7 (inlined Layout::for_value_raw::<[T]>) { + let mut _5: usize; + let mut _6: usize; + scope 8 { + scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { + let mut _7: std::ptr::Alignment; + } + } + scope 9 (inlined size_of_val_raw::<[T]>) { + } + scope 10 (inlined align_of_val_raw::<[T]>) { + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); + StorageLive(_4); + _3 = copy _2 as *mut [T] (Transmute); + _4 = copy _2 as *const [T] (Transmute); + StorageLive(_6); + _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageLive(_7); + _7 = copy _6 as std::ptr::Alignment (Transmute); + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); + StorageDead(_6); + StorageDead(_4); + switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + } + + bb3: { + StorageLive(_9); + _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_11); + StorageLive(_10); + _10 = discriminant(_8); + _11 = move _10 as usize (IntToInt); + StorageDead(_10); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_11); + StorageDead(_9); + goto -> bb5; + } + + bb5: { + StorageDead(_2); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir new file mode 100644 index 000000000000..e88f6b394fa7 --- /dev/null +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -0,0 +1,111 @@ +// MIR for `generic_in_place` after PreCodegen + +fn generic_in_place(_1: *mut Box<[T]>) -> () { + debug ptr => _1; + let mut _0: (); + scope 1 (inlined drop_in_place::> - shim(Some(Box<[T]>))) { + scope 2 (inlined as Drop>::drop) { + let _2: std::ptr::NonNull<[T]>; + let mut _3: *mut [T]; + let mut _4: *const [T]; + let _12: (); + scope 3 { + let _8: std::ptr::alignment::AlignmentEnum; + scope 4 { + scope 12 (inlined Layout::size) { + } + scope 13 (inlined Unique::<[T]>::cast::) { + scope 14 (inlined NonNull::<[T]>::cast::) { + scope 15 (inlined NonNull::<[T]>::as_ptr) { + } + } + } + scope 16 (inlined as From>>::from) { + scope 17 (inlined Unique::::as_non_null_ptr) { + } + } + scope 18 (inlined ::deallocate) { + let mut _9: *mut u8; + scope 19 (inlined Layout::size) { + } + scope 20 (inlined NonNull::::as_ptr) { + } + scope 21 (inlined std::alloc::dealloc) { + let mut _11: usize; + scope 22 (inlined Layout::size) { + } + scope 23 (inlined Layout::align) { + scope 24 (inlined std::ptr::Alignment::as_usize) { + let mut _10: u64; + } + } + } + } + } + scope 5 (inlined Unique::<[T]>::as_ptr) { + scope 6 (inlined NonNull::<[T]>::as_ptr) { + } + } + scope 7 (inlined Layout::for_value_raw::<[T]>) { + let mut _5: usize; + let mut _6: usize; + scope 8 { + scope 11 (inlined #[track_caller] Layout::from_size_align_unchecked) { + let mut _7: std::ptr::Alignment; + } + } + scope 9 (inlined size_of_val_raw::<[T]>) { + } + scope 10 (inlined align_of_val_raw::<[T]>) { + } + } + } + } + } + + bb0: { + StorageLive(_2); + _2 = copy (((*_1).0: std::ptr::Unique<[T]>).0: std::ptr::NonNull<[T]>); + StorageLive(_4); + _3 = copy _2 as *mut [T] (Transmute); + _4 = copy _2 as *const [T] (Transmute); + StorageLive(_6); + _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + } + + bb1: { + _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; + } + + bb2: { + StorageLive(_7); + _7 = copy _6 as std::ptr::Alignment (Transmute); + _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); + StorageDead(_7); + StorageDead(_6); + StorageDead(_4); + switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + } + + bb3: { + StorageLive(_9); + _9 = copy _3 as *mut u8 (PtrToPtr); + StorageLive(_11); + StorageLive(_10); + _10 = discriminant(_8); + _11 = move _10 as usize (IntToInt); + StorageDead(_10); + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + } + + bb4: { + StorageDead(_11); + StorageDead(_9); + goto -> bb5; + } + + bb5: { + StorageDead(_2); + return; + } +} diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs new file mode 100644 index 000000000000..e625a440d200 --- /dev/null +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -0,0 +1,19 @@ +//@ compile-flags: -O -Zmir-opt-level=2 +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// EMIT_MIR_FOR_EACH_BIT_WIDTH + +#![crate_type = "lib"] + +// EMIT_MIR drop_boxed_slice.generic_in_place.PreCodegen.after.mir +pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { + // CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>) + // CHECK: (inlined as Drop>::drop) + // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> + // CHECK: [[ALIGN:_.+]] = std::intrinsics::align_of_val::<[T]> + // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); + // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); + // CHECK: [[D:_.+]] = discriminant([[C]]); + // CHECK: [[E:_.+]] = move [[D]] as usize (IntToInt); + // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[E]]) -> + std::ptr::drop_in_place(ptr) +} From 185b0745180e6f6f3a4c9c7a41ec8725749cf5bd Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 23:23:22 -0700 Subject: [PATCH 157/201] Add a MIR test for `align_of_val` on a slice --- ..._slice.InstSimplify-after-simplifycfg.diff | 20 +++++++++++++++++++ tests/mir-opt/instsimplify/align_of_slice.rs | 13 ++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff create mode 100644 tests/mir-opt/instsimplify/align_of_slice.rs diff --git a/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff new file mode 100644 index 000000000000..21ba2e250560 --- /dev/null +++ b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff @@ -0,0 +1,20 @@ +- // MIR for `of_val_slice` before InstSimplify-after-simplifycfg ++ // MIR for `of_val_slice` after InstSimplify-after-simplifycfg + + fn of_val_slice(_1: &[T]) -> usize { + debug slice => _1; + let mut _0: usize; + let mut _2: *const [T]; + + bb0: { + StorageLive(_2); + _2 = &raw const (*_1); + _0 = std::intrinsics::align_of_val::<[T]>(move _2) -> [return: bb1, unwind unreachable]; + } + + bb1: { + StorageDead(_2); + return; + } + } + diff --git a/tests/mir-opt/instsimplify/align_of_slice.rs b/tests/mir-opt/instsimplify/align_of_slice.rs new file mode 100644 index 000000000000..900a05c2a8b3 --- /dev/null +++ b/tests/mir-opt/instsimplify/align_of_slice.rs @@ -0,0 +1,13 @@ +//@ test-mir-pass: InstSimplify-after-simplifycfg +//@ needs-unwind + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +// EMIT_MIR align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff +pub fn of_val_slice(slice: &[T]) -> usize { + // CHECK-LABEL: fn of_val_slice(_1: &[T]) + // CHECK: _2 = &raw const (*_1); + // CHECK: _0 = std::intrinsics::align_of_val::<[T]>(move _2) + unsafe { core::intrinsics::align_of_val(slice) } +} From 8ef9233339c09d80e12b31c523082b94d9e09b44 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 27 Jul 2025 23:39:06 -0700 Subject: [PATCH 158/201] =?UTF-8?q?Simplify=20`align=5Fof=5Fval::<[T]>(?= =?UTF-8?q?=E2=80=A6)`=20=E2=86=92=20`align=5Fof::()`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rustc_mir_transform/src/instsimplify.rs | 31 +++++++++++++++++++ ..._slice.InstSimplify-after-simplifycfg.diff | 4 ++- tests/mir-opt/instsimplify/align_of_slice.rs | 3 +- ...ace.PreCodegen.after.32bit.panic-abort.mir | 25 +++++++-------- ...ce.PreCodegen.after.32bit.panic-unwind.mir | 25 +++++++-------- ...ace.PreCodegen.after.64bit.panic-abort.mir | 25 +++++++-------- ...ce.PreCodegen.after.64bit.panic-unwind.mir | 25 +++++++-------- tests/mir-opt/pre-codegen/drop_boxed_slice.rs | 2 +- 8 files changed, 80 insertions(+), 60 deletions(-) diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index dbcaed209538..c83bd25c6636 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -55,6 +55,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { let terminator = block.terminator.as_mut().unwrap(); ctx.simplify_primitive_clone(terminator, &mut block.statements); + ctx.simplify_align_of_slice_val(terminator, &mut block.statements); ctx.simplify_intrinsic_assert(terminator); ctx.simplify_nounwind_call(terminator); simplify_duplicate_switch_targets(terminator); @@ -252,6 +253,36 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { terminator.kind = TerminatorKind::Goto { target: *destination_block }; } + // Convert `align_of_val::<[T]>(ptr)` to `align_of::()`, since the + // alignment of a slice doesn't actually depend on metadata at all + // and the element type is always `Sized`. + // + // This is here so it can run after inlining, where it's more useful. + // (LowerIntrinsics is done in cleanup, before the optimization passes.) + fn simplify_align_of_slice_val( + &self, + terminator: &mut Terminator<'tcx>, + statements: &mut Vec>, + ) { + if let TerminatorKind::Call { + func, args, destination, target: Some(destination_block), .. + } = &terminator.kind + && args.len() == 1 + && let Some((fn_def_id, generics)) = func.const_fn_def() + && self.tcx.is_intrinsic(fn_def_id, sym::align_of_val) + && let ty::Slice(elem_ty) = *generics.type_at(0).kind() + { + statements.push(Statement::new( + terminator.source_info, + StatementKind::Assign(Box::new(( + *destination, + Rvalue::NullaryOp(NullOp::AlignOf, elem_ty), + ))), + )); + terminator.kind = TerminatorKind::Goto { target: *destination_block }; + } + } + fn simplify_nounwind_call(&self, terminator: &mut Terminator<'tcx>) { let TerminatorKind::Call { ref func, ref mut unwind, .. } = terminator.kind else { return; diff --git a/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff index 21ba2e250560..042e19bf2aa1 100644 --- a/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff +++ b/tests/mir-opt/instsimplify/align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff @@ -9,7 +9,9 @@ bb0: { StorageLive(_2); _2 = &raw const (*_1); - _0 = std::intrinsics::align_of_val::<[T]>(move _2) -> [return: bb1, unwind unreachable]; +- _0 = std::intrinsics::align_of_val::<[T]>(move _2) -> [return: bb1, unwind unreachable]; ++ _0 = AlignOf(T); ++ goto -> bb1; } bb1: { diff --git a/tests/mir-opt/instsimplify/align_of_slice.rs b/tests/mir-opt/instsimplify/align_of_slice.rs index 900a05c2a8b3..0af05cb6b0b0 100644 --- a/tests/mir-opt/instsimplify/align_of_slice.rs +++ b/tests/mir-opt/instsimplify/align_of_slice.rs @@ -7,7 +7,6 @@ // EMIT_MIR align_of_slice.of_val_slice.InstSimplify-after-simplifycfg.diff pub fn of_val_slice(slice: &[T]) -> usize { // CHECK-LABEL: fn of_val_slice(_1: &[T]) - // CHECK: _2 = &raw const (*_1); - // CHECK: _0 = std::intrinsics::align_of_val::<[T]>(move _2) + // CHECK: _0 = AlignOf(T); unsafe { core::intrinsics::align_of_val(slice) } } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir index 587e69154ea1..2777bba893bb 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir @@ -70,24 +70,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); StorageLive(_6); - _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; - } - - bb2: { + _6 = AlignOf(T); StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); StorageDead(_7); StorageDead(_6); StorageDead(_4); - switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } - bb3: { + bb2: { StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_11); @@ -95,16 +92,16 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _10 = discriminant(_8); _11 = move _10 as usize (IntToInt); StorageDead(_10); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_11); + StorageDead(_9); + goto -> bb4; } bb4: { - StorageDead(_11); - StorageDead(_9); - goto -> bb5; - } - - bb5: { StorageDead(_2); return; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir index 587e69154ea1..2777bba893bb 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir @@ -70,24 +70,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); StorageLive(_6); - _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; - } - - bb2: { + _6 = AlignOf(T); StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); StorageDead(_7); StorageDead(_6); StorageDead(_4); - switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } - bb3: { + bb2: { StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_11); @@ -95,16 +92,16 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _10 = discriminant(_8); _11 = move _10 as usize (IntToInt); StorageDead(_10); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_11); + StorageDead(_9); + goto -> bb4; } bb4: { - StorageDead(_11); - StorageDead(_9); - goto -> bb5; - } - - bb5: { StorageDead(_2); return; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir index e88f6b394fa7..2be0a478c852 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir @@ -70,24 +70,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); StorageLive(_6); - _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; - } - - bb2: { + _6 = AlignOf(T); StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); StorageDead(_7); StorageDead(_6); StorageDead(_4); - switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } - bb3: { + bb2: { StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_11); @@ -95,16 +92,16 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _10 = discriminant(_8); _11 = move _10 as usize (IntToInt); StorageDead(_10); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_11); + StorageDead(_9); + goto -> bb4; } bb4: { - StorageDead(_11); - StorageDead(_9); - goto -> bb5; - } - - bb5: { StorageDead(_2); return; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir index e88f6b394fa7..2be0a478c852 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir @@ -70,24 +70,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _3 = copy _2 as *mut [T] (Transmute); _4 = copy _2 as *const [T] (Transmute); StorageLive(_6); - _5 = std::intrinsics::size_of_val::<[T]>(copy _4) -> [return: bb1, unwind unreachable]; + _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = std::intrinsics::align_of_val::<[T]>(move _4) -> [return: bb2, unwind unreachable]; - } - - bb2: { + _6 = AlignOf(T); StorageLive(_7); _7 = copy _6 as std::ptr::Alignment (Transmute); _8 = move (_7.0: std::ptr::alignment::AlignmentEnum); StorageDead(_7); StorageDead(_6); StorageDead(_4); - switchInt(copy _5) -> [0: bb5, otherwise: bb3]; + switchInt(copy _5) -> [0: bb4, otherwise: bb2]; } - bb3: { + bb2: { StorageLive(_9); _9 = copy _3 as *mut u8 (PtrToPtr); StorageLive(_11); @@ -95,16 +92,16 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () { _10 = discriminant(_8); _11 = move _10 as usize (IntToInt); StorageDead(_10); - _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb4, unwind unreachable]; + _12 = alloc::alloc::__rust_dealloc(move _9, move _5, move _11) -> [return: bb3, unwind unreachable]; + } + + bb3: { + StorageDead(_11); + StorageDead(_9); + goto -> bb4; } bb4: { - StorageDead(_11); - StorageDead(_9); - goto -> bb5; - } - - bb5: { StorageDead(_2); return; } diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs index e625a440d200..11fb7afef0fb 100644 --- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs +++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs @@ -9,7 +9,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) { // CHECK-LABEL: fn generic_in_place(_1: *mut Box<[T]>) // CHECK: (inlined as Drop>::drop) // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]> - // CHECK: [[ALIGN:_.+]] = std::intrinsics::align_of_val::<[T]> + // CHECK: [[ALIGN:_.+]] = AlignOf(T); // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute); // CHECK: [[C:_.+]] = move ([[B]].0: std::ptr::alignment::AlignmentEnum); // CHECK: [[D:_.+]] = discriminant([[C]]); From 81176b1a6c3a40b2db42821f209cf834befad77e Mon Sep 17 00:00:00 2001 From: xizheyin Date: Tue, 22 Jul 2025 00:20:52 +0800 Subject: [PATCH 159/201] Create two methods to fix `find_oldest_ancestor_in_same_ctxt` Signed-off-by: xizheyin --- .../src/diagnostics/explain_borrow.rs | 2 +- .../src/fn_ctxt/suggestions.rs | 12 ++- compiler/rustc_lint/src/unused.rs | 4 +- compiler/rustc_span/src/lib.rs | 84 +++++++++++-------- 4 files changed, 59 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index a10da08ddf34..fdca6b565409 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -341,7 +341,7 @@ impl<'tcx> BorrowExplanation<'tcx> { } } } else if let LocalInfo::BlockTailTemp(info) = local_decl.local_info() { - let sp = info.span.find_oldest_ancestor_in_same_ctxt(); + let sp = info.span.find_ancestor_not_from_macro().unwrap_or(info.span); if info.tail_result_is_ignored { // #85581: If the first mutable borrow's scope contains // the second borrow, this suggestion isn't helpful. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 33ae4f6c45cf..2345cdab208e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1302,7 +1302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => ".clone()".to_string(), }; - let span = expr.span.find_oldest_ancestor_in_same_ctxt().shrink_to_hi(); + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span).shrink_to_hi(); diag.span_suggestion_verbose( span, @@ -1395,7 +1395,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .macro_backtrace() .any(|x| matches!(x.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, ..))) { - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr + .span + .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map()) + .unwrap_or(expr.span); let mut sugg = if self.precedence(expr) >= ExprPrecedence::Unambiguous { vec![(span.shrink_to_hi(), ".into()".to_owned())] @@ -2062,7 +2065,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None => sugg.to_string(), }; - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr + .span + .find_ancestor_not_from_extern_macro(&self.tcx.sess.source_map()) + .unwrap_or(expr.span); err.span_suggestion_verbose(span.shrink_to_hi(), msg, sugg, Applicability::HasPlaceholders); true } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 11df071f0686..00e40b515a32 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { let mut op_warned = false; if let Some(must_use_op) = must_use_op { - let span = expr.span.find_oldest_ancestor_in_same_ctxt(); + let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span); cx.emit_span_lint( UNUSED_MUST_USE, expr.span, @@ -511,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { ); } MustUsePath::Def(span, def_id, reason) => { - let span = span.find_oldest_ancestor_in_same_ctxt(); + let span = span.find_ancestor_not_from_macro().unwrap_or(*span); cx.emit_span_lint( UNUSED_MUST_USE, span, diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index dbc67da37b53..3f72ccd9f89d 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -716,12 +716,17 @@ impl Span { (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site) } - /// Walk down the expansion ancestors to find a span that's contained within `outer`. + /// Find the first ancestor span that's contained within `outer`. /// - /// The span returned by this method may have a different [`SyntaxContext`] as `outer`. + /// This method traverses the macro expansion ancestors until it finds the first span + /// that's contained within `outer`. + /// + /// The span returned by this method may have a different [`SyntaxContext`] than `outer`. /// If you need to extend the span, use [`find_ancestor_inside_same_ctxt`] instead, /// because joining spans with different syntax contexts can create unexpected results. /// + /// This is used to find the span of the macro call when a parent expr span, i.e. `outer`, is known. + /// /// [`find_ancestor_inside_same_ctxt`]: Self::find_ancestor_inside_same_ctxt pub fn find_ancestor_inside(mut self, outer: Span) -> Option { while !outer.contains(self) { @@ -730,8 +735,10 @@ impl Span { Some(self) } - /// Walk down the expansion ancestors to find a span with the same [`SyntaxContext`] as - /// `other`. + /// Find the first ancestor span with the same [`SyntaxContext`] as `other`. + /// + /// This method traverses the macro expansion ancestors until it finds a span + /// that has the same [`SyntaxContext`] as `other`. /// /// Like [`find_ancestor_inside_same_ctxt`], but specifically for when spans might not /// overlap. Take care when using this, and prefer [`find_ancestor_inside`] or @@ -747,9 +754,12 @@ impl Span { Some(self) } - /// Walk down the expansion ancestors to find a span that's contained within `outer` and + /// Find the first ancestor span that's contained within `outer` and /// has the same [`SyntaxContext`] as `outer`. /// + /// This method traverses the macro expansion ancestors until it finds a span + /// that is both contained within `outer` and has the same [`SyntaxContext`] as `outer`. + /// /// This method is the combination of [`find_ancestor_inside`] and /// [`find_ancestor_in_same_ctxt`] and should be preferred when extending the returned span. /// If you do not need to modify the span, use [`find_ancestor_inside`] instead. @@ -763,43 +773,43 @@ impl Span { Some(self) } - /// Recursively walk down the expansion ancestors to find the oldest ancestor span with the same - /// [`SyntaxContext`] the initial span. + /// Find the first ancestor span that does not come from an external macro. /// - /// This method is suitable for peeling through *local* macro expansions to find the "innermost" - /// span that is still local and shares the same [`SyntaxContext`]. For example, given + /// This method traverses the macro expansion ancestors until it finds a span + /// that is either from user-written code or from a local macro (defined in the current crate). /// - /// ```ignore (illustrative example, contains type error) - /// macro_rules! outer { - /// ($x: expr) => { - /// inner!($x) - /// } - /// } + /// External macros are those defined in dependencies or the standard library. + /// This method is useful for reporting errors in user-controllable code and avoiding + /// diagnostics inside external macros. /// - /// macro_rules! inner { - /// ($x: expr) => { - /// format!("error: {}", $x) - /// //~^ ERROR mismatched types - /// } - /// } + /// # See also /// - /// fn bar(x: &str) -> Result<(), Box> { - /// Err(outer!(x)) - /// } - /// ``` - /// - /// if provided the initial span of `outer!(x)` inside `bar`, this method will recurse - /// the parent callsites until we reach `format!("error: {}", $x)`, at which point it is the - /// oldest ancestor span that is both still local and shares the same [`SyntaxContext`] as the - /// initial span. - pub fn find_oldest_ancestor_in_same_ctxt(self) -> Span { - let mut cur = self; - while cur.eq_ctxt(self) - && let Some(parent_callsite) = cur.parent_callsite() - { - cur = parent_callsite; + /// - [`Self::find_ancestor_not_from_macro`] + /// - [`Self::in_external_macro`] + pub fn find_ancestor_not_from_extern_macro(mut self, sm: &SourceMap) -> Option { + while self.in_external_macro(sm) { + self = self.parent_callsite()?; } - cur + Some(self) + } + + /// Find the first ancestor span that does not come from any macro expansion. + /// + /// This method traverses the macro expansion ancestors until it finds a span + /// that originates from user-written code rather than any macro-generated code. + /// + /// This method is useful for reporting errors at the exact location users wrote code + /// and providing suggestions at directly editable locations. + /// + /// # See also + /// + /// - [`Self::find_ancestor_not_from_extern_macro`] + /// - [`Span::from_expansion`] + pub fn find_ancestor_not_from_macro(mut self) -> Option { + while self.from_expansion() { + self = self.parent_callsite()?; + } + Some(self) } /// Edition of the crate from which this span came. From e532080507056b242ecbcbb8e79bc95f041998d6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 09:22:24 +0200 Subject: [PATCH 160/201] cc dependencies: clarify comment --- compiler/rustc_codegen_ssa/Cargo.toml | 4 ++-- compiler/rustc_llvm/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index cfae1b3ec98e..94501da69a76 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -8,8 +8,8 @@ edition = "2024" ar_archive_writer = "0.4.2" bitflags = "2.4.1" bstr = "1.11.3" -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# `cc` in `rustc_llvm` if you update the `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_llvm`. cc = "=1.2.16" itertools = "0.12" pathdiff = "0.2.0" diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml index 39de4783238b..85a2a9c09f08 100644 --- a/compiler/rustc_llvm/Cargo.toml +++ b/compiler/rustc_llvm/Cargo.toml @@ -10,8 +10,8 @@ libc = "0.2.73" [build-dependencies] # tidy-alphabetical-start -# Pinned so `cargo update` bumps don't cause breakage. Please also update the -# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here. +# `cc` updates often break things, so we pin it here. Cargo enforces "max 1 semver-compat version +# per crate", so if you change this, you need to also change it in `rustc_codegen_ssa`. cc = "=1.2.16" # tidy-alphabetical-end From b8302ce6053e97899002324ef1eefcbaf41984fc Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Tue, 29 Jul 2025 09:03:57 +0200 Subject: [PATCH 161/201] Add a regression test for an ICE with the `generic_const_exprs` feature attribute. It ensures that using the `generic_const_exprs` feature in a library crate without enabling it in a dependent crate does not lead to an ICE. --- ...bute-missing-in-dependent-crate-ice-aux.rs | 9 +++++++++ ...ttribute-missing-in-dependent-crate-ice.rs | 19 +++++++++++++++++++ ...bute-missing-in-dependent-crate-ice.stderr | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs create mode 100644 tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr diff --git a/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs b/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs new file mode 100644 index 000000000000..3902454c14cd --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/auxiliary/feature-attribute-missing-in-dependent-crate-ice-aux.rs @@ -0,0 +1,9 @@ +#![feature(generic_const_exprs)] + +pub struct Error(()); + +pub trait FromSlice: Sized { + const SIZE: usize = std::mem::size_of::(); + + fn validate_slice(bytes: &[[u8; Self::SIZE]]) -> Result<(), Error>; +} diff --git a/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs new file mode 100644 index 000000000000..b9537014767e --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.rs @@ -0,0 +1,19 @@ +//! Regression test to ensure that using the `generic_const_exprs` feature in a library crate +//! without enabling it in a dependent crate does not lead to an ICE. +//! +//! Issue: + +//@ aux-build:feature-attribute-missing-in-dependent-crate-ice-aux.rs + +extern crate feature_attribute_missing_in_dependent_crate_ice_aux as aux; + +struct Wrapper(i64); + +impl aux::FromSlice for Wrapper { + fn validate_slice(_: &[[u8; Self::SIZE]]) -> Result<(), aux::Error> { + //~^ ERROR generic `Self` types are currently not permitted in anonymous constants + Ok(()) + } +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr new file mode 100644 index 000000000000..5c3306651426 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/feature-attribute-missing-in-dependent-crate-ice.stderr @@ -0,0 +1,14 @@ +error: generic `Self` types are currently not permitted in anonymous constants + --> $DIR/feature-attribute-missing-in-dependent-crate-ice.rs:13:33 + | +LL | fn validate_slice(_: &[[u8; Self::SIZE]]) -> Result<(), aux::Error> { + | ^^^^ + | +note: not a concrete type + --> $DIR/feature-attribute-missing-in-dependent-crate-ice.rs:12:41 + | +LL | impl aux::FromSlice for Wrapper { + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + From ec4dc1c5f87e3de735cefb21ac51522568b1b391 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 13:34:04 +0200 Subject: [PATCH 162/201] clean up existing poison files --- library/std/src/sync/poison.rs | 6 +++--- library/std/src/sync/poison/condvar.rs | 2 +- library/std/src/sync/poison/mutex.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 0c05f152ef84..b901a5701a48 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -13,7 +13,9 @@ //! depend on the primitive. See [#Overview] below. //! //! For the alternative implementations that do not employ poisoning, -//! see `std::sync::nonpoisoning`. +//! see [`std::sync::nonpoison`]. +//! +//! [`std::sync::nonpoison`]: crate::sync::nonpoison //! //! # Overview //! @@ -56,8 +58,6 @@ //! while it is locked exclusively (write mode). If a panic occurs in any reader, //! then the lock will not be poisoned. -// FIXME(sync_nonpoison) add links to sync::nonpoison to the doc comment above. - #[stable(feature = "rust1", since = "1.0.0")] pub use self::condvar::{Condvar, WaitTimeoutResult}; #[unstable(feature = "mapped_lock_guards", issue = "117108")] diff --git a/library/std/src/sync/poison/condvar.rs b/library/std/src/sync/poison/condvar.rs index 7f0f3f652bcb..0e9d4233c657 100644 --- a/library/std/src/sync/poison/condvar.rs +++ b/library/std/src/sync/poison/condvar.rs @@ -13,7 +13,7 @@ use crate::time::{Duration, Instant}; #[stable(feature = "wait_timeout", since = "1.5.0")] pub struct WaitTimeoutResult(bool); -// FIXME(sync_nonpoison): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. +// FIXME(nonpoison_condvar): `WaitTimeoutResult` is actually poisoning-agnostic, it seems. // Should we take advantage of this fact? impl WaitTimeoutResult { /// Returns `true` if the wait was known to have timed out. diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 30325be685c3..64744f18c746 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -650,7 +650,7 @@ impl fmt::Debug for Mutex { d.field("data", &&**err.get_ref()); } Err(TryLockError::WouldBlock) => { - d.field("data", &format_args!("")); + d.field("data", &""); } } d.field("poisoned", &self.poison.get()); From 3bdc228c103d72a61d9639d6fd0dd5c1566ec37f Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 13:47:49 +0200 Subject: [PATCH 163/201] add `nonpoison::mutex` implementation Adds the equivalent `nonpoison` types to the `poison::mutex` module. These types and implementations are gated under the `nonpoison_mutex` feature gate. Also blesses the ui tests that now have a name conflicts (because these types no longer have unique names). The full path distinguishes the different types. Co-authored-by: Aandreba Co-authored-by: Trevor Gross --- library/std/src/sync/mod.rs | 2 + library/std/src/sync/nonpoison.rs | 37 ++ library/std/src/sync/nonpoison/mutex.rs | 611 ++++++++++++++++++ .../issue-64130-non-send-future-diags.stderr | 4 +- tests/ui/async-await/issue-71137.stderr | 4 +- tests/ui/async-await/issues/issue-67893.rs | 2 +- .../ui/async-await/issues/issue-67893.stderr | 6 +- tests/ui/lint/must_not_suspend/mutex.rs | 2 +- tests/ui/lint/must_not_suspend/mutex.stderr | 2 +- .../private-field-access-in-mutex-54062.rs | 2 +- ...private-field-access-in-mutex-54062.stderr | 2 +- tests/ui/suggestions/inner_type.fixed | 2 +- tests/ui/suggestions/inner_type.rs | 2 +- tests/ui/suggestions/inner_type.stderr | 4 +- tests/ui/sync/mutexguard-sync.stderr | 2 +- .../const-traits/span-bug-issue-121418.stderr | 6 +- .../ui/typeck/assign-non-lval-derefmut.fixed | 4 +- tests/ui/typeck/assign-non-lval-derefmut.rs | 4 +- .../ui/typeck/assign-non-lval-derefmut.stderr | 14 +- tests/ui/typeck/deref-multi.stderr | 2 +- 20 files changed, 682 insertions(+), 32 deletions(-) create mode 100644 library/std/src/sync/nonpoison.rs create mode 100644 library/std/src/sync/nonpoison/mutex.rs diff --git a/library/std/src/sync/mod.rs b/library/std/src/sync/mod.rs index e67b4f6f22f5..6ef3bf25cf67 100644 --- a/library/std/src/sync/mod.rs +++ b/library/std/src/sync/mod.rs @@ -225,6 +225,8 @@ pub use self::poison::{MappedMutexGuard, MappedRwLockReadGuard, MappedRwLockWrit pub mod mpmc; pub mod mpsc; +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub mod nonpoison; #[unstable(feature = "sync_poison_mod", issue = "134646")] pub mod poison; diff --git a/library/std/src/sync/nonpoison.rs b/library/std/src/sync/nonpoison.rs new file mode 100644 index 000000000000..2bbf226dc2cd --- /dev/null +++ b/library/std/src/sync/nonpoison.rs @@ -0,0 +1,37 @@ +//! Non-poisoning synchronous locks. +//! +//! The difference from the locks in the [`poison`] module is that the locks in this module will not +//! become poisoned when a thread panics while holding a guard. +//! +//! [`poison`]: super::poison + +use crate::fmt; + +/// A type alias for the result of a nonblocking locking method. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub type TryLockResult = Result; + +/// A lock could not be acquired at this time because the operation would otherwise block. +#[unstable(feature = "sync_nonpoison", issue = "134645")] +pub struct WouldBlock; + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Debug for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "WouldBlock".fmt(f) + } +} + +#[unstable(feature = "sync_nonpoison", issue = "134645")] +impl fmt::Display for WouldBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "try_lock failed because the operation would block".fmt(f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +pub use self::mutex::MappedMutexGuard; +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +pub use self::mutex::{Mutex, MutexGuard}; + +mod mutex; diff --git a/library/std/src/sync/nonpoison/mutex.rs b/library/std/src/sync/nonpoison/mutex.rs new file mode 100644 index 000000000000..b6861c78f001 --- /dev/null +++ b/library/std/src/sync/nonpoison/mutex.rs @@ -0,0 +1,611 @@ +use crate::cell::UnsafeCell; +use crate::fmt; +use crate::marker::PhantomData; +use crate::mem::{self, ManuallyDrop}; +use crate::ops::{Deref, DerefMut}; +use crate::ptr::NonNull; +use crate::sync::nonpoison::{TryLockResult, WouldBlock}; +use crate::sys::sync as sys; + +/// A mutual exclusion primitive useful for protecting shared data that does not keep track of +/// lock poisoning. +/// +/// For more information about mutexes, check out the documentation for the poisoning variant of +/// this lock at [`poison::Mutex`]. +/// +/// [`poison::Mutex`]: crate::sync::poison::Mutex +/// +/// # Examples +/// +/// Note that this `Mutex` does **not** propagate threads that panic while holding the lock via +/// poisoning. If you need this functionality, see [`poison::Mutex`]. +/// +/// ``` +/// #![feature(nonpoison_mutex)] +/// +/// use std::thread; +/// use std::sync::{Arc, nonpoison::Mutex}; +/// +/// let mutex = Arc::new(Mutex::new(0u32)); +/// let mut handles = Vec::new(); +/// +/// for n in 0..10 { +/// let m = Arc::clone(&mutex); +/// let handle = thread::spawn(move || { +/// let mut guard = m.lock(); +/// *guard += 1; +/// panic!("panic from thread {n} {guard}") +/// }); +/// handles.push(handle); +/// } +/// +/// for h in handles { +/// let _ = h.join(); +/// } +/// +/// println!("Finished, locked {} times", mutex.lock()); +/// ``` +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutex")] +pub struct Mutex { + inner: sys::Mutex, + data: UnsafeCell, +} + +/// `T` must be `Send` for a [`Mutex`] to be `Send` because it is possible to acquire +/// the owned `T` from the `Mutex` via [`into_inner`]. +/// +/// [`into_inner`]: Mutex::into_inner +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Send for Mutex {} + +/// `T` must be `Send` for [`Mutex`] to be `Sync`. +/// This ensures that the protected data can be accessed safely from multiple threads +/// without causing data races or other unsafe behavior. +/// +/// [`Mutex`] provides mutable access to `T` to one thread at a time. However, it's essential +/// for `T` to be `Send` because it's not safe for non-`Send` structures to be accessed in +/// this manner. For instance, consider [`Rc`], a non-atomic reference counted smart pointer, +/// which is not `Send`. With `Rc`, we can have multiple copies pointing to the same heap +/// allocation with a non-atomic reference count. If we were to use `Mutex>`, it would +/// only protect one instance of `Rc` from shared access, leaving other copies vulnerable +/// to potential data races. +/// +/// Also note that it is not necessary for `T` to be `Sync` as `&T` is only made available +/// to one thread at a time if `T` is not `Sync`. +/// +/// [`Rc`]: crate::rc::Rc +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for Mutex {} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`lock`] and [`try_lock`] methods on +/// [`Mutex`]. +/// +/// [`lock`]: Mutex::lock +/// [`try_lock`]: Mutex::try_lock +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +#[cfg_attr(not(test), rustc_diagnostic_item = "NonPoisonMutexGuard")] +pub struct MutexGuard<'a, T: ?Sized + 'a> { + lock: &'a Mutex, +} + +/// A [`MutexGuard`] is not `Send` to maximize platform portablity. +/// +/// On platforms that use POSIX threads (commonly referred to as pthreads) there is a requirement to +/// release mutex locks on the same thread they were acquired. +/// For this reason, [`MutexGuard`] must not implement `Send` to prevent it being dropped from +/// another thread. +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MutexGuard<'_, T> {} + +/// `T` must be `Sync` for a [`MutexGuard`] to be `Sync` +/// because it is possible to get a `&T` from `&MutexGuard` (via `Deref`). +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MutexGuard<'_, T> {} + +// FIXME(nonpoison_condvar): Use this link instead: [`Condvar`]: crate::sync::nonpoison::Condvar +/// An RAII mutex guard returned by `MutexGuard::map`, which can point to a +/// subfield of the protected data. When this structure is dropped (falls out +/// of scope), the lock will be unlocked. +/// +/// The main difference between `MappedMutexGuard` and [`MutexGuard`] is that the +/// former cannot be used with [`Condvar`], since that could introduce soundness issues if the +/// locked object is modified by another thread while the `Mutex` is unlocked. +/// +/// The data protected by the mutex can be accessed through this guard via its +/// [`Deref`] and [`DerefMut`] implementations. +/// +/// This structure is created by the [`map`] and [`filter_map`] methods on +/// [`MutexGuard`]. +/// +/// [`map`]: MutexGuard::map +/// [`filter_map`]: MutexGuard::filter_map +/// [`Condvar`]: crate::sync::Condvar +#[must_use = "if unused the Mutex will immediately unlock"] +#[must_not_suspend = "holding a MappedMutexGuard across suspend \ + points can cause deadlocks, delays, \ + and cause Futures to not implement `Send`"] +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +#[clippy::has_significant_drop] +pub struct MappedMutexGuard<'a, T: ?Sized + 'a> { + // NB: we use a pointer instead of `&'a mut T` to avoid `noalias` violations, because a + // `MappedMutexGuard` argument doesn't hold uniqueness for its whole scope, only until it drops. + // `NonNull` is covariant over `T`, so we add a `PhantomData<&'a mut T>` field + // below for the correct variance over `T` (invariance). + data: NonNull, + inner: &'a sys::Mutex, + _variance: PhantomData<&'a mut T>, +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl !Send for MappedMutexGuard<'_, T> {} +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +// #[unstable(feature = "nonpoison_mutex", issue = "134645")] +unsafe impl Sync for MappedMutexGuard<'_, T> {} + +impl Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + #[inline] + pub const fn new(t: T) -> Mutex { + Mutex { inner: sys::Mutex::new(), data: UnsafeCell::new(t) } + } + + /// Returns the contained value by cloning it. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_cloned(&self) -> T + where + T: Clone, + { + self.lock().clone() + } + + /// Sets the contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.get_cloned(), 7); + /// mutex.set(11); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn set(&self, value: T) { + if mem::needs_drop::() { + // If the contained value has a non-trivial destructor, we + // call that destructor after the lock has been released. + drop(self.replace(value)) + } else { + *self.lock() = value; + } + } + + /// Replaces the contained value with `value`, and returns the old contained value. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// #![feature(lock_value_accessors)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(7); + /// + /// assert_eq!(mutex.replace(11), 7); + /// assert_eq!(mutex.get_cloned(), 11); + /// ``` + #[unstable(feature = "lock_value_accessors", issue = "133407")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn replace(&self, value: T) -> T { + let mut guard = self.lock(); + mem::replace(&mut *guard, value) + } +} + +impl Mutex { + /// Acquires a mutex, blocking the current thread until it is able to do so. + /// + /// This function will block the local thread until it is available to acquire + /// the mutex. Upon returning, the thread is the only thread with the lock + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// The exact behavior on locking a mutex in the thread which already holds + /// the lock is left unspecified. However, this function will not return on + /// the second call (it might panic or deadlock, for example). + /// + /// # Panics + /// + /// This function might panic when called if the lock is already held by + /// the current thread. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::{Arc, nonpoison::Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// *c_mutex.lock() = 10; + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn lock(&self) -> MutexGuard<'_, T> { + unsafe { + self.inner.lock(); + MutexGuard::new(self) + } + } + + /// Attempts to acquire this lock. + /// + /// This function does not block. If the lock could not be acquired at this time, then + /// [`WouldBlock`] is returned. Otherwise, an RAII guard is returned. + /// + /// The lock will be unlocked when the guard is dropped. + /// + /// # Errors + /// + /// If the mutex could not be acquired because it is already locked, then this call will return + /// the [`WouldBlock`] error. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Mutex}; + /// use std::thread; + /// + /// let mutex = Arc::new(Mutex::new(0)); + /// let c_mutex = Arc::clone(&mutex); + /// + /// thread::spawn(move || { + /// let mut lock = c_mutex.try_lock(); + /// if let Ok(ref mut mutex) = lock { + /// **mutex = 10; + /// } else { + /// println!("try_lock failed"); + /// } + /// }).join().expect("thread::spawn failed"); + /// assert_eq!(*mutex.lock().unwrap(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn try_lock(&self) -> TryLockResult> { + unsafe { if self.inner.try_lock() { Ok(MutexGuard::new(self)) } else { Err(WouldBlock) } } + } + + /// Consumes this mutex, returning the underlying data. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mutex = Mutex::new(0); + /// assert_eq!(mutex.into_inner(), 0); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn into_inner(self) -> T + where + T: Sized, + { + self.data.into_inner() + } + + /// Returns a mutable reference to the underlying data. + /// + /// Since this call borrows the `Mutex` mutably, no actual locking needs to + /// take place -- the mutable borrow statically guarantees no locks exist. + /// + /// # Examples + /// + /// ``` + /// #![feature(nonpoison_mutex)] + /// + /// use std::sync::nonpoison::Mutex; + /// + /// let mut mutex = Mutex::new(0); + /// *mutex.get_mut() = 10; + /// assert_eq!(*mutex.lock(), 10); + /// ``` + #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn get_mut(&mut self) -> &mut T { + self.data.get_mut() + } + + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the mutex is dropped. + #[unstable(feature = "mutex_data_ptr", issue = "140368")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl From for Mutex { + /// Creates a new mutex in an unlocked state ready for use. + /// This is equivalent to [`Mutex::new`]. + fn from(t: T) -> Self { + Mutex::new(t) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Default for Mutex { + /// Creates a `Mutex`, with the `Default` value for T. + fn default() -> Mutex { + Mutex::new(Default::default()) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for Mutex { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut d = f.debug_struct("Mutex"); + match self.try_lock() { + Ok(guard) => { + d.field("data", &&*guard); + } + Err(WouldBlock) => { + d.field("data", &""); + } + } + d.finish_non_exhaustive() + } +} + +impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { + unsafe fn new(lock: &'mutex Mutex) -> MutexGuard<'mutex, T> { + return MutexGuard { lock }; + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Deref for MutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl DerefMut for MutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.lock.data.get() } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl Drop for MutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.lock.inner.unlock(); + } + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Debug for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "nonpoison_mutex", issue = "134645")] +impl fmt::Display for MutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { &mut *orig.lock.data.get() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { &mut *orig.lock.data.get() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: &orig.lock.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Deref for MappedMutexGuard<'_, T> { + type Target = T; + + fn deref(&self) -> &T { + unsafe { self.data.as_ref() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl DerefMut for MappedMutexGuard<'_, T> { + fn deref_mut(&mut self) -> &mut T { + unsafe { self.data.as_mut() } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl Drop for MappedMutexGuard<'_, T> { + #[inline] + fn drop(&mut self) { + unsafe { + self.inner.unlock(); + } + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Debug for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[unstable(feature = "mapped_lock_guards", issue = "117108")] +impl fmt::Display for MappedMutexGuard<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl<'a, T: ?Sized> MappedMutexGuard<'a, T> { + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data, e.g. + /// an enum variant. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn map(mut orig: Self, f: F) -> MappedMutexGuard<'a, U> + where + F: FnOnce(&mut T) -> &mut U, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + let data = NonNull::from(f(unsafe { orig.data.as_mut() })); + let orig = ManuallyDrop::new(orig); + MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData } + } + + /// Makes a [`MappedMutexGuard`] for a component of the borrowed data. The + /// original guard is returned as an `Err(...)` if the closure returns + /// `None`. + /// + /// The `Mutex` is already locked, so this cannot fail. + /// + /// This is an associated function that needs to be used as + /// `MappedMutexGuard::filter_map(...)`. A method would interfere with methods of the + /// same name on the contents of the `MutexGuard` used through `Deref`. + #[unstable(feature = "mapped_lock_guards", issue = "117108")] + // #[unstable(feature = "nonpoison_mutex", issue = "134645")] + pub fn filter_map(mut orig: Self, f: F) -> Result, Self> + where + F: FnOnce(&mut T) -> Option<&mut U>, + U: ?Sized, + { + // SAFETY: the conditions of `MutexGuard::new` were satisfied when the original guard + // was created, and have been upheld throughout `map` and/or `filter_map`. + // The signature of the closure guarantees that it will not "leak" the lifetime of the reference + // passed to it. If the closure panics, the guard will be dropped. + match f(unsafe { orig.data.as_mut() }) { + Some(data) => { + let data = NonNull::from(data); + let orig = ManuallyDrop::new(orig); + Ok(MappedMutexGuard { data, inner: orig.inner, _variance: PhantomData }) + } + None => Err(orig), + } + } +} diff --git a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr index d28807e223be..beaf8e9c96d8 100644 --- a/tests/ui/async-await/issue-64130-non-send-future-diags.stderr +++ b/tests/ui/async-await/issue-64130-non-send-future-diags.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | is_send(foo()); | ^^^^^ future returned by `foo` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, u32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, u32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-non-send-future-diags.rs:17:11 | LL | let g = x.lock().unwrap(); - | - has type `MutexGuard<'_, u32>` which is not `Send` + | - has type `std::sync::MutexGuard<'_, u32>` which is not `Send` LL | baz().await; | ^^^^^ await occurs here, with `g` maybe used later note: required by a bound in `is_send` diff --git a/tests/ui/async-await/issue-71137.stderr b/tests/ui/async-await/issue-71137.stderr index 8739c22a3104..d567e3f2063d 100644 --- a/tests/ui/async-await/issue-71137.stderr +++ b/tests/ui/async-await/issue-71137.stderr @@ -4,12 +4,12 @@ error: future cannot be sent between threads safely LL | fake_spawn(wrong_mutex()); | ^^^^^^^^^^^^^ future returned by `wrong_mutex` is not `Send` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, i32>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, i32>` note: future is not `Send` as this value is used across an await --> $DIR/issue-71137.rs:14:26 | LL | let mut guard = m.lock().unwrap(); - | --------- has type `MutexGuard<'_, i32>` which is not `Send` + | --------- has type `std::sync::MutexGuard<'_, i32>` which is not `Send` LL | (async { "right"; }).await; | ^^^^^ await occurs here, with `mut guard` maybe used later note: required by a bound in `fake_spawn` diff --git a/tests/ui/async-await/issues/issue-67893.rs b/tests/ui/async-await/issues/issue-67893.rs index 73cce38c94a0..2020abe7a5a9 100644 --- a/tests/ui/async-await/issues/issue-67893.rs +++ b/tests/ui/async-await/issues/issue-67893.rs @@ -7,5 +7,5 @@ fn g(_: impl Send) {} fn main() { g(issue_67893::run()) - //~^ ERROR `MutexGuard<'_, ()>` cannot be sent between threads safely + //~^ ERROR `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely } diff --git a/tests/ui/async-await/issues/issue-67893.stderr b/tests/ui/async-await/issues/issue-67893.stderr index c01237255b89..34f28dd53c7b 100644 --- a/tests/ui/async-await/issues/issue-67893.stderr +++ b/tests/ui/async-await/issues/issue-67893.stderr @@ -1,8 +1,8 @@ -error[E0277]: `MutexGuard<'_, ()>` cannot be sent between threads safely +error[E0277]: `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely --> $DIR/issue-67893.rs:9:7 | LL | g(issue_67893::run()) - | - ^^^^^^^^^^^^^^^^^^ `MutexGuard<'_, ()>` cannot be sent between threads safely + | - ^^^^^^^^^^^^^^^^^^ `std::sync::MutexGuard<'_, ()>` cannot be sent between threads safely | | | required by a bound introduced by this call | @@ -11,7 +11,7 @@ LL | g(issue_67893::run()) LL | pub async fn run() { | ------------------ within this `impl Future` | - = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` + = help: within `impl Future`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, ()>` note: required because it's used within this `async` fn body --> $DIR/auxiliary/issue_67893.rs:9:20 | diff --git a/tests/ui/lint/must_not_suspend/mutex.rs b/tests/ui/lint/must_not_suspend/mutex.rs index d14f7130b4cf..8dd4cc176151 100644 --- a/tests/ui/lint/must_not_suspend/mutex.rs +++ b/tests/ui/lint/must_not_suspend/mutex.rs @@ -5,7 +5,7 @@ async fn other() {} pub async fn uhoh(m: std::sync::Mutex<()>) { - let _guard = m.lock().unwrap(); //~ ERROR `MutexGuard` held across + let _guard = m.lock().unwrap(); //~ ERROR `std::sync::MutexGuard` held across other().await; } diff --git a/tests/ui/lint/must_not_suspend/mutex.stderr b/tests/ui/lint/must_not_suspend/mutex.stderr index ca53a753150e..0db1f2575b19 100644 --- a/tests/ui/lint/must_not_suspend/mutex.stderr +++ b/tests/ui/lint/must_not_suspend/mutex.stderr @@ -1,4 +1,4 @@ -error: `MutexGuard` held across a suspend point, but should not be +error: `std::sync::MutexGuard` held across a suspend point, but should not be --> $DIR/mutex.rs:8:9 | LL | let _guard = m.lock().unwrap(); diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.rs b/tests/ui/privacy/private-field-access-in-mutex-54062.rs index f145f9c3e529..c957e0bc7e86 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.rs +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.rs @@ -8,7 +8,7 @@ fn main() {} fn testing(test: Test) { let _ = test.comps.inner.try_lock(); - //~^ ERROR: field `inner` of struct `Mutex` is private + //~^ ERROR: field `inner` of struct `std::sync::Mutex` is private } // https://github.com/rust-lang/rust/issues/54062 diff --git a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr index 142441075974..f7f846406485 100644 --- a/tests/ui/privacy/private-field-access-in-mutex-54062.stderr +++ b/tests/ui/privacy/private-field-access-in-mutex-54062.stderr @@ -1,4 +1,4 @@ -error[E0616]: field `inner` of struct `Mutex` is private +error[E0616]: field `inner` of struct `std::sync::Mutex` is private --> $DIR/private-field-access-in-mutex-54062.rs:10:24 | LL | let _ = test.comps.inner.try_lock(); diff --git a/tests/ui/suggestions/inner_type.fixed b/tests/ui/suggestions/inner_type.fixed index 3dc939d6b5c4..175a2a02acdf 100644 --- a/tests/ui/suggestions/inner_type.fixed +++ b/tests/ui/suggestions/inner_type.fixed @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.lock().unwrap().method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.rs b/tests/ui/suggestions/inner_type.rs index 81a05c253119..ab021414f56c 100644 --- a/tests/ui/suggestions/inner_type.rs +++ b/tests/ui/suggestions/inner_type.rs @@ -25,7 +25,7 @@ fn main() { let another_item = std::sync::Mutex::new(Struct { p: 42_u32 }); another_item.method(); - //~^ ERROR no method named `method` found for struct `Mutex` in the current scope [E0599] + //~^ ERROR no method named `method` found for struct `std::sync::Mutex` in the current scope [E0599] //~| HELP use `.lock().unwrap()` to borrow the `Struct`, blocking the current thread until it can be acquired let another_item = std::sync::RwLock::new(Struct { p: 42_u32 }); diff --git a/tests/ui/suggestions/inner_type.stderr b/tests/ui/suggestions/inner_type.stderr index 5ac3d04f1041..67ebb5789b70 100644 --- a/tests/ui/suggestions/inner_type.stderr +++ b/tests/ui/suggestions/inner_type.stderr @@ -30,11 +30,11 @@ help: use `.borrow_mut()` to mutably borrow the `Struct`, panicking if any LL | other_item.borrow_mut().some_mutable_method(); | +++++++++++++ -error[E0599]: no method named `method` found for struct `Mutex` in the current scope +error[E0599]: no method named `method` found for struct `std::sync::Mutex` in the current scope --> $DIR/inner_type.rs:27:18 | LL | another_item.method(); - | ^^^^^^ method not found in `Mutex>` + | ^^^^^^ method not found in `std::sync::Mutex>` | note: the method `method` exists on the type `Struct` --> $DIR/inner_type.rs:9:5 diff --git a/tests/ui/sync/mutexguard-sync.stderr b/tests/ui/sync/mutexguard-sync.stderr index 1501a793d5e8..ab9983c1f2c1 100644 --- a/tests/ui/sync/mutexguard-sync.stderr +++ b/tests/ui/sync/mutexguard-sync.stderr @@ -8,7 +8,7 @@ LL | test_sync(guard); | = help: the trait `Sync` is not implemented for `Cell` = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead - = note: required for `MutexGuard<'_, Cell>` to implement `Sync` + = note: required for `std::sync::MutexGuard<'_, Cell>` to implement `Sync` note: required by a bound in `test_sync` --> $DIR/mutexguard-sync.rs:5:17 | diff --git a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr index 92cfecd05404..f31129d9cb7c 100644 --- a/tests/ui/traits/const-traits/span-bug-issue-121418.stderr +++ b/tests/ui/traits/const-traits/span-bug-issue-121418.stderr @@ -14,8 +14,8 @@ error[E0277]: the size for values of type `(dyn T + 'static)` cannot be known at LL | pub const fn new() -> std::sync::Mutex {} | ^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | - = help: within `Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` -note: required because it appears within the type `Mutex<(dyn T + 'static)>` + = help: within `std::sync::Mutex<(dyn T + 'static)>`, the trait `Sized` is not implemented for `(dyn T + 'static)` +note: required because it appears within the type `std::sync::Mutex<(dyn T + 'static)>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL = note: the return type of a function must have a statically known size @@ -27,7 +27,7 @@ LL | pub const fn new() -> std::sync::Mutex {} | | | implicitly returns `()` as its body has no tail or `return` expression | - = note: expected struct `Mutex<(dyn T + 'static)>` + = note: expected struct `std::sync::Mutex<(dyn T + 'static)>` found unit type `()` error: aborting due to 3 previous errors diff --git a/tests/ui/typeck/assign-non-lval-derefmut.fixed b/tests/ui/typeck/assign-non-lval-derefmut.fixed index 6ecec574f2ec..e6f97a9e86c6 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.fixed +++ b/tests/ui/typeck/assign-non-lval-derefmut.fixed @@ -5,11 +5,11 @@ fn main() { *x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment *x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); *y = 2; //~^ ERROR mismatched types *y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.rs b/tests/ui/typeck/assign-non-lval-derefmut.rs index ac1be913e2a9..a53a52c7e4dd 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.rs +++ b/tests/ui/typeck/assign-non-lval-derefmut.rs @@ -5,11 +5,11 @@ fn main() { x.lock().unwrap() = 2; //~^ ERROR invalid left-hand side of assignment x.lock().unwrap() += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` let mut y = x.lock().unwrap(); y = 2; //~^ ERROR mismatched types y += 1; - //~^ ERROR binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` + //~^ ERROR binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` } diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr index 16fb1e9c5c3a..f57b5abe2eed 100644 --- a/tests/ui/typeck/assign-non-lval-derefmut.stderr +++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr @@ -11,15 +11,15 @@ help: consider dereferencing here to assign to the mutably borrowed value LL | *x.lock().unwrap() = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:7:5 | LL | x.lock().unwrap() += 1; | -----------------^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` @@ -36,22 +36,22 @@ LL | let mut y = x.lock().unwrap(); LL | y = 2; | ^ expected `MutexGuard<'_, usize>`, found integer | - = note: expected struct `MutexGuard<'_, usize>` + = note: expected struct `std::sync::MutexGuard<'_, usize>` found type `{integer}` help: consider dereferencing here to assign to the mutably borrowed value | LL | *y = 2; | + -error[E0368]: binary assignment operation `+=` cannot be applied to type `MutexGuard<'_, usize>` +error[E0368]: binary assignment operation `+=` cannot be applied to type `std::sync::MutexGuard<'_, usize>` --> $DIR/assign-non-lval-derefmut.rs:13:5 | LL | y += 1; | -^^^^^ | | - | cannot use `+=` on type `MutexGuard<'_, usize>` + | cannot use `+=` on type `std::sync::MutexGuard<'_, usize>` | -note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` +note: the foreign item type `std::sync::MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>` --> $SRC_DIR/std/src/sync/poison/mutex.rs:LL:COL | = note: not implement `AddAssign<{integer}>` diff --git a/tests/ui/typeck/deref-multi.stderr b/tests/ui/typeck/deref-multi.stderr index 02513853c486..c4fa49e43ef2 100644 --- a/tests/ui/typeck/deref-multi.stderr +++ b/tests/ui/typeck/deref-multi.stderr @@ -63,7 +63,7 @@ LL | x.lock().unwrap() | ^^^^^^^^^^^^^^^^^ expected `i32`, found `MutexGuard<'_, &i32>` | = note: expected type `i32` - found struct `MutexGuard<'_, &i32>` + found struct `std::sync::MutexGuard<'_, &i32>` help: consider dereferencing the type | LL | **x.lock().unwrap() From f34fb05923ab4c8bff48bf5d52ca08c5b8e2233a Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 14:04:32 +0200 Subject: [PATCH 164/201] reorder mutex tests This commit simply helps discern the actual changes needed to test both poison and nonpoison locks. --- library/std/tests/sync/mutex.rs | 312 +++++++++++++++++--------------- 1 file changed, 163 insertions(+), 149 deletions(-) diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index ac82914d6de4..6f06d3385d1a 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -6,28 +6,9 @@ use std::sync::mpsc::channel; use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError}; use std::{hint, mem, thread}; -struct Packet(Arc<(Mutex, Condvar)>); - -#[derive(Eq, PartialEq, Debug)] -struct NonCopy(i32); - -#[derive(Eq, PartialEq, Debug)] -struct NonCopyNeedsDrop(i32); - -impl Drop for NonCopyNeedsDrop { - fn drop(&mut self) { - hint::black_box(()); - } -} - -#[test] -fn test_needs_drop() { - assert!(!mem::needs_drop::()); - assert!(mem::needs_drop::()); -} - -#[derive(Clone, Eq, PartialEq, Debug)] -struct Cloneable(i32); +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Nonpoison & Poison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// #[test] fn smoke() { @@ -78,19 +59,22 @@ fn try_lock() { *m.try_lock().unwrap() = (); } -fn new_poisoned_mutex(value: T) -> Mutex { - let mutex = Mutex::new(value); +#[derive(Eq, PartialEq, Debug)] +struct NonCopy(i32); - let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { - let _guard = mutex.lock().unwrap(); +#[derive(Eq, PartialEq, Debug)] +struct NonCopyNeedsDrop(i32); - panic!("test panic to poison mutex"); - })); +impl Drop for NonCopyNeedsDrop { + fn drop(&mut self) { + hint::black_box(()); + } +} - assert!(catch_unwind_result.is_err()); - assert!(mutex.is_poisoned()); - - mutex +#[test] +fn test_needs_drop() { + assert!(!mem::needs_drop::()); + assert!(mem::needs_drop::()); } #[test] @@ -117,35 +101,6 @@ fn test_into_inner_drop() { assert_eq!(num_drops.load(Ordering::SeqCst), 1); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_into_inner_poison() { - let m = new_poisoned_mutex(NonCopy(10)); - - match m.into_inner() { - Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), - Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), - } -} - -#[test] -fn test_get_cloned() { - let m = Mutex::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_get_cloned_poison() { - let m = new_poisoned_mutex(Cloneable(10)); - - match m.get_cloned() { - Err(e) => assert_eq!(e.into_inner(), ()), - Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), - } -} - #[test] fn test_get_mut() { let mut m = Mutex::new(NonCopy(10)); @@ -154,14 +109,13 @@ fn test_get_mut() { } #[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_get_mut_poison() { - let mut m = new_poisoned_mutex(NonCopy(10)); +fn test_get_cloned() { + #[derive(Clone, Eq, PartialEq, Debug)] + struct Cloneable(i32); - match m.get_mut() { - Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), - Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), - } + let m = Mutex::new(Cloneable(10)); + + assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); } #[test] @@ -181,6 +135,145 @@ fn test_set() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } +#[test] +fn test_replace() { + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*m.lock().unwrap(), init()); + assert_eq!(m.replace(value()).unwrap(), init()); + assert_eq!(*m.lock().unwrap(), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); +} + +#[test] +fn test_mutex_arc_condvar() { + struct Packet(Arc<(Mutex, Condvar)>); + + let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); + let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + // wait until parent gets in + rx.recv().unwrap(); + let &(ref lock, ref cvar) = &*packet2.0; + let mut lock = lock.lock().unwrap(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*packet.0; + let mut lock = lock.lock().unwrap(); + tx.send(()).unwrap(); + assert!(!*lock); + while !*lock { + lock = cvar.wait(lock).unwrap(); + } +} + +#[test] +fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = arc2.lock().unwrap(); + let lock2 = lock.lock().unwrap(); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); +} + +#[test] +fn test_mutex_unsized() { + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *mutex.lock().unwrap(); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*mutex.lock().unwrap(), comp); +} + +#[test] +fn test_mapping_mapped_guard() { + let arr = [0; 4]; + let mut lock = Mutex::new(arr); + let guard = lock.lock().unwrap(); + let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Poison Tests +//////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Creates a mutex that is immediately poisoned. +fn new_poisoned_mutex(value: T) -> Mutex { + let mutex = Mutex::new(value); + + let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard = mutex.lock().unwrap(); + + panic!("test panic to poison mutex"); + })); + + assert!(catch_unwind_result.is_err()); + assert!(mutex.is_poisoned()); + + mutex +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_into_inner_poison() { + let m = new_poisoned_mutex(NonCopy(10)); + + match m.into_inner() { + Err(e) => assert_eq!(e.into_inner(), NonCopy(10)), + Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_get_cloned_poison() { + #[derive(Clone, Eq, PartialEq, Debug)] + struct Cloneable(i32); + + let m = new_poisoned_mutex(Cloneable(10)); + + match m.get_cloned() { + Err(e) => assert_eq!(e.into_inner(), ()), + Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"), + } +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn test_get_mut_poison() { + let mut m = new_poisoned_mutex(NonCopy(10)); + + match m.get_mut() { + Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)), + Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"), + } +} + #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_set_poison() { @@ -203,23 +296,6 @@ fn test_set_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.lock().unwrap(), value()); - } - - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_replace_poison() { @@ -242,32 +318,11 @@ fn test_replace_poison() { inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); } -#[test] -fn test_mutex_arc_condvar() { - let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); - let packet2 = Packet(packet.0.clone()); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - // wait until parent gets in - rx.recv().unwrap(); - let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock().unwrap(); - *lock = true; - cvar.notify_one(); - }); - - let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); - tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - lock = cvar.wait(lock).unwrap(); - } -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_arc_condvar_poison() { + struct Packet(Arc<(Mutex, Condvar)>); + let packet = Packet(Arc::new((Mutex::new(1), Condvar::new()))); let packet2 = Packet(packet.0.clone()); let (tx, rx) = channel(); @@ -326,22 +381,6 @@ fn test_mutex_arc_poison_mapped() { assert!(arc.is_poisoned()); } -#[test] -fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock2 = lock.lock().unwrap(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn test_mutex_arc_access_in_unwind() { @@ -364,31 +403,6 @@ fn test_mutex_arc_access_in_unwind() { assert_eq!(*lock, 2); } -#[test] -fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock().unwrap(); - b[0] = 4; - b[2] = 5; - } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock().unwrap(), comp); -} - -#[test] -fn test_mapping_mapped_guard() { - let arr = [0; 4]; - let mut lock = Mutex::new(arr); - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); - let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); - assert_eq!(guard.len(), 1); - guard[0] = 42; - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_unlocked_poison() { From 3eb722e655670cc204aee5e3b4f0a9b9f29a8cd0 Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 23 Jul 2025 14:09:27 +0200 Subject: [PATCH 165/201] add nonpoison and poison mutex tests Adds tests for the `nonpoison::Mutex` variant by using a macro to duplicate the existing `poison` tests. Note that all of the tests here are adapted from the existing `poison` tests. --- library/std/tests/sync/lib.rs | 55 +++++ library/std/tests/sync/mutex.rs | 351 +++++++++++++++++++------------- 2 files changed, 260 insertions(+), 146 deletions(-) diff --git a/library/std/tests/sync/lib.rs b/library/std/tests/sync/lib.rs index 51190f0894fb..94f1fe96b6a2 100644 --- a/library/std/tests/sync/lib.rs +++ b/library/std/tests/sync/lib.rs @@ -6,7 +6,10 @@ #![feature(reentrant_lock)] #![feature(rwlock_downgrade)] #![feature(std_internals)] +#![feature(sync_nonpoison)] +#![feature(nonpoison_mutex)] #![allow(internal_features)] +#![feature(macro_metavar_expr_concat)] // For concatenating identifiers in macros. mod barrier; mod condvar; @@ -29,3 +32,55 @@ mod rwlock; #[path = "../common/mod.rs"] mod common; + +#[track_caller] +fn result_unwrap(x: Result) -> T { + x.unwrap() +} + +/// A macro that generates two test cases for both the poison and nonpoison locks. +/// +/// To write a test that tests both `poison` and `nonpoison` locks, import any of the types +/// under both `poison` and `nonpoison` using the module name `locks` instead. For example, write +/// `use locks::Mutex;` instead of `use std::sync::poiosn::Mutex`. This will import the correct type +/// for each test variant. +/// +/// Write a test as normal in the `test_body`, but instead of calling `unwrap` on `poison` methods +/// that return a `LockResult` or similar, call the function `maybe_unwrap(...)` on the result. +/// +/// For example, call `maybe_unwrap(mutex.lock())` instead of `mutex.lock().unwrap()` or +/// `maybe_unwrap(rwlock.read())` instead of `rwlock.read().unwrap()`. +/// +/// For the `poison` types, `maybe_unwrap` will simply unwrap the `Result` (usually this is a form +/// of `LockResult`, but it could also be other kinds of results). For the `nonpoison` types, it is +/// a no-op (the identity function). +/// +/// The test names will be prefiex with `poison_` or `nonpoison_`. +macro_rules! nonpoison_and_poison_unwrap_test { + ( + name: $name:ident, + test_body: {$($test_body:tt)*} + ) => { + // Creates the nonpoison test. + #[test] + fn ${concat(nonpoison_, $name)}() { + #[allow(unused_imports)] + use ::std::convert::identity as maybe_unwrap; + use ::std::sync::nonpoison as locks; + + $($test_body)* + } + + // Creates the poison test with the suffix `_unwrap_poisoned`. + #[test] + fn ${concat(poison_, $name)}() { + #[allow(unused_imports)] + use super::result_unwrap as maybe_unwrap; + use ::std::sync::poison as locks; + + $($test_body)* + } + } +} + +use nonpoison_and_poison_unwrap_test; diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 6f06d3385d1a..60f5d4f45bf2 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -9,55 +9,68 @@ use std::{hint, mem, thread}; //////////////////////////////////////////////////////////////////////////////////////////////////// // Nonpoison & Poison Tests //////////////////////////////////////////////////////////////////////////////////////////////////// +use super::nonpoison_and_poison_unwrap_test; -#[test] -fn smoke() { - let m = Mutex::new(()); - drop(m.lock().unwrap()); - drop(m.lock().unwrap()); -} +nonpoison_and_poison_unwrap_test!( + name: smoke, + test_body: { + use locks::Mutex; -#[test] -fn lots_and_lots() { - const J: u32 = 1000; - const K: u32 = 3; + let m = Mutex::new(()); + drop(maybe_unwrap(m.lock())); + drop(maybe_unwrap(m.lock())); + } +); - let m = Arc::new(Mutex::new(0)); +nonpoison_and_poison_unwrap_test!( + name: lots_and_lots, + test_body: { + use locks::Mutex; - fn inc(m: &Mutex) { - for _ in 0..J { - *m.lock().unwrap() += 1; + const J: u32 = 1000; + const K: u32 = 3; + + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex) { + for _ in 0..J { + *maybe_unwrap(m.lock()) += 1; + } } - } - let (tx, rx) = channel(); - for _ in 0..K { - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - let tx2 = tx.clone(); - let m2 = m.clone(); - thread::spawn(move || { - inc(&m2); - tx2.send(()).unwrap(); - }); - } + let (tx, rx) = channel(); + for _ in 0..K { + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + let tx2 = tx.clone(); + let m2 = m.clone(); + thread::spawn(move || { + inc(&m2); + tx2.send(()).unwrap(); + }); + } - drop(tx); - for _ in 0..2 * K { - rx.recv().unwrap(); + drop(tx); + for _ in 0..2 * K { + rx.recv().unwrap(); + } + assert_eq!(*maybe_unwrap(m.lock()), J * K * 2); } - assert_eq!(*m.lock().unwrap(), J * K * 2); -} +); -#[test] -fn try_lock() { - let m = Mutex::new(()); - *m.try_lock().unwrap() = (); -} +nonpoison_and_poison_unwrap_test!( + name: try_lock, + test_body: { + use locks::Mutex; + + let m = Mutex::new(()); + *m.try_lock().unwrap() = (); + } +); #[derive(Eq, PartialEq, Debug)] struct NonCopy(i32); @@ -77,146 +90,192 @@ fn test_needs_drop() { assert!(mem::needs_drop::()); } -#[test] -fn test_into_inner() { - let m = Mutex::new(NonCopy(10)); - assert_eq!(m.into_inner().unwrap(), NonCopy(10)); -} +nonpoison_and_poison_unwrap_test!( + name: test_into_inner, + test_body: { + use locks::Mutex; -#[test] -fn test_into_inner_drop() { - struct Foo(Arc); - impl Drop for Foo { - fn drop(&mut self) { - self.0.fetch_add(1, Ordering::SeqCst); + let m = Mutex::new(NonCopy(10)); + assert_eq!(maybe_unwrap(m.into_inner()), NonCopy(10)); + } +); + +nonpoison_and_poison_unwrap_test!( + name: test_into_inner_drop, + test_body: { + use locks::Mutex; + + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } } - } - let num_drops = Arc::new(AtomicUsize::new(0)); - let m = Mutex::new(Foo(num_drops.clone())); - assert_eq!(num_drops.load(Ordering::SeqCst), 0); - { - let _inner = m.into_inner().unwrap(); + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); assert_eq!(num_drops.load(Ordering::SeqCst), 0); + { + let _inner = maybe_unwrap(m.into_inner()); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + } + assert_eq!(num_drops.load(Ordering::SeqCst), 1); } - assert_eq!(num_drops.load(Ordering::SeqCst), 1); -} +); -#[test] -fn test_get_mut() { - let mut m = Mutex::new(NonCopy(10)); - *m.get_mut().unwrap() = NonCopy(20); - assert_eq!(m.into_inner().unwrap(), NonCopy(20)); -} +nonpoison_and_poison_unwrap_test!( + name: test_get_mut, + test_body: { + use locks::Mutex; -#[test] -fn test_get_cloned() { - #[derive(Clone, Eq, PartialEq, Debug)] - struct Cloneable(i32); - - let m = Mutex::new(Cloneable(10)); - - assert_eq!(m.get_cloned().unwrap(), Cloneable(10)); -} - -#[test] -fn test_set() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); - - assert_eq!(*m.lock().unwrap(), init()); - m.set(value()).unwrap(); - assert_eq!(*m.lock().unwrap(), value()); + let mut m = Mutex::new(NonCopy(10)); + *maybe_unwrap(m.get_mut()) = NonCopy(20); + assert_eq!(maybe_unwrap(m.into_inner()), NonCopy(20)); } +); - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} +nonpoison_and_poison_unwrap_test!( + name: test_get_cloned, + test_body: { + use locks::Mutex; -#[test] -fn test_replace() { - fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) - where - T: Debug + Eq, - { - let m = Mutex::new(init()); + #[derive(Clone, Eq, PartialEq, Debug)] + struct Cloneable(i32); - assert_eq!(*m.lock().unwrap(), init()); - assert_eq!(m.replace(value()).unwrap(), init()); - assert_eq!(*m.lock().unwrap(), value()); + let m = Mutex::new(Cloneable(10)); + + assert_eq!(maybe_unwrap(m.get_cloned()), Cloneable(10)); } +); - inner(|| NonCopy(10), || NonCopy(20)); - inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); -} +nonpoison_and_poison_unwrap_test!( + name: test_set, + test_body: { + use locks::Mutex; + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*maybe_unwrap(m.lock()), init()); + maybe_unwrap(m.set(value())); + assert_eq!(*maybe_unwrap(m.lock()), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); + } +); + +// Ensure that old values that are replaced by `set` are correctly dropped. +nonpoison_and_poison_unwrap_test!( + name: test_replace, + test_body: { + use locks::Mutex; + + fn inner(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T) + where + T: Debug + Eq, + { + let m = Mutex::new(init()); + + assert_eq!(*maybe_unwrap(m.lock()), init()); + assert_eq!(maybe_unwrap(m.replace(value())), init()); + assert_eq!(*maybe_unwrap(m.lock()), value()); + } + + inner(|| NonCopy(10), || NonCopy(20)); + inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20)); + } +); + +// FIXME(nonpoison_condvar): Move this to the `condvar.rs` test file once `nonpoison::condvar` gets +// implemented. #[test] fn test_mutex_arc_condvar() { struct Packet(Arc<(Mutex, Condvar)>); let packet = Packet(Arc::new((Mutex::new(false), Condvar::new()))); let packet2 = Packet(packet.0.clone()); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { - // wait until parent gets in + // Wait until our parent has taken the lock. rx.recv().unwrap(); let &(ref lock, ref cvar) = &*packet2.0; - let mut lock = lock.lock().unwrap(); - *lock = true; + + // Set the data to `true` and wake up our parent. + let mut guard = lock.lock().unwrap(); + *guard = true; cvar.notify_one(); }); let &(ref lock, ref cvar) = &*packet.0; - let mut lock = lock.lock().unwrap(); + let mut guard = lock.lock().unwrap(); + // Wake up our child. tx.send(()).unwrap(); - assert!(!*lock); - while !*lock { - lock = cvar.wait(lock).unwrap(); + + // Wait until our child has set the data to `true`. + assert!(!*guard); + while !*guard { + guard = cvar.wait(guard).unwrap(); } } -#[test] -fn test_mutex_arc_nested() { - // Tests nested mutexes and access - // to underlying data. - let arc = Arc::new(Mutex::new(1)); - let arc2 = Arc::new(Mutex::new(arc)); - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - let lock = arc2.lock().unwrap(); - let lock2 = lock.lock().unwrap(); - assert_eq!(*lock2, 1); - tx.send(()).unwrap(); - }); - rx.recv().unwrap(); -} +nonpoison_and_poison_unwrap_test!( + name: test_mutex_arc_nested, + test_body: { + use locks::Mutex; -#[test] -fn test_mutex_unsized() { - let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); - { - let b = &mut *mutex.lock().unwrap(); - b[0] = 4; - b[2] = 5; + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + let _t = thread::spawn(move || { + let lock = maybe_unwrap(arc2.lock()); + let lock2 = maybe_unwrap(lock.lock()); + assert_eq!(*lock2, 1); + tx.send(()).unwrap(); + }); + rx.recv().unwrap(); } - let comp: &[i32] = &[4, 2, 5]; - assert_eq!(&*mutex.lock().unwrap(), comp); -} +); -#[test] -fn test_mapping_mapped_guard() { - let arr = [0; 4]; - let mut lock = Mutex::new(arr); - let guard = lock.lock().unwrap(); - let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); - let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); - assert_eq!(guard.len(), 1); - guard[0] = 42; - drop(guard); - assert_eq!(*lock.get_mut().unwrap(), [0, 42, 0, 0]); -} +nonpoison_and_poison_unwrap_test!( + name: test_mutex_unsized, + test_body: { + use locks::Mutex; + + let mutex: &Mutex<[i32]> = &Mutex::new([1, 2, 3]); + { + let b = &mut *maybe_unwrap(mutex.lock()); + b[0] = 4; + b[2] = 5; + } + let comp: &[i32] = &[4, 2, 5]; + assert_eq!(&*maybe_unwrap(mutex.lock()), comp); + } +); + +nonpoison_and_poison_unwrap_test!( + name: test_mapping_mapped_guard, + test_body: { + use locks::{Mutex, MutexGuard, MappedMutexGuard}; + + let arr = [0; 4]; + let lock = Mutex::new(arr); + let guard = maybe_unwrap(lock.lock()); + let guard = MutexGuard::map(guard, |arr| &mut arr[..2]); + let mut guard = MappedMutexGuard::map(guard, |slice| &mut slice[1..]); + assert_eq!(guard.len(), 1); + guard[0] = 42; + drop(guard); + assert_eq!(*maybe_unwrap(lock.lock()), [0, 42, 0, 0]); + } +); //////////////////////////////////////////////////////////////////////////////////////////////////// // Poison Tests From d073d297b65f8cc0c8b75249b74c7df5a3dc18bc Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Tue, 29 Jul 2025 10:44:36 +0200 Subject: [PATCH 166/201] add extra drop, panic, and unwind tests --- library/std/tests/sync/mutex.rs | 102 +++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 22 deletions(-) diff --git a/library/std/tests/sync/mutex.rs b/library/std/tests/sync/mutex.rs index 60f5d4f45bf2..90cefc0d5946 100644 --- a/library/std/tests/sync/mutex.rs +++ b/library/std/tests/sync/mutex.rs @@ -111,6 +111,7 @@ nonpoison_and_poison_unwrap_test!( self.0.fetch_add(1, Ordering::SeqCst); } } + let num_drops = Arc::new(AtomicUsize::new(0)); let m = Mutex::new(Foo(num_drops.clone())); assert_eq!(num_drops.load(Ordering::SeqCst), 0); @@ -169,6 +170,28 @@ nonpoison_and_poison_unwrap_test!( ); // Ensure that old values that are replaced by `set` are correctly dropped. +nonpoison_and_poison_unwrap_test!( + name: test_set_drop, + test_body: { + use locks::Mutex; + + struct Foo(Arc); + impl Drop for Foo { + fn drop(&mut self) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } + + let num_drops = Arc::new(AtomicUsize::new(0)); + let m = Mutex::new(Foo(num_drops.clone())); + assert_eq!(num_drops.load(Ordering::SeqCst), 0); + + let different = Foo(Arc::new(AtomicUsize::new(42))); + maybe_unwrap(m.set(different)); + assert_eq!(num_drops.load(Ordering::SeqCst), 1); + } +); + nonpoison_and_poison_unwrap_test!( name: test_replace, test_body: { @@ -277,6 +300,63 @@ nonpoison_and_poison_unwrap_test!( } ); +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +nonpoison_and_poison_unwrap_test!( + name: test_panics, + test_body: { + use locks::Mutex; + + let mutex = Mutex::new(42); + + let catch_unwind_result1 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard1 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex once"); + })); + assert!(catch_unwind_result1.is_err()); + + let catch_unwind_result2 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard2 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex twice"); + })); + assert!(catch_unwind_result2.is_err()); + + let catch_unwind_result3 = panic::catch_unwind(AssertUnwindSafe(|| { + let _guard3 = maybe_unwrap(mutex.lock()); + + panic!("test panic with mutex thrice"); + })); + assert!(catch_unwind_result3.is_err()); + } +); + +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +nonpoison_and_poison_unwrap_test!( + name: test_mutex_arc_access_in_unwind, + test_body: { + use locks::Mutex; + + let arc = Arc::new(Mutex::new(1)); + let arc2 = arc.clone(); + let _ = thread::spawn(move || -> () { + struct Unwinder { + i: Arc>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *maybe_unwrap(self.i.lock()) += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }) + .join(); + let lock = maybe_unwrap(arc.lock()); + assert_eq!(*lock, 2); + } +); + //////////////////////////////////////////////////////////////////////////////////////////////////// // Poison Tests //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -440,28 +520,6 @@ fn test_mutex_arc_poison_mapped() { assert!(arc.is_poisoned()); } -#[test] -#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn test_mutex_arc_access_in_unwind() { - let arc = Arc::new(Mutex::new(1)); - let arc2 = arc.clone(); - let _ = thread::spawn(move || -> () { - struct Unwinder { - i: Arc>, - } - impl Drop for Unwinder { - fn drop(&mut self) { - *self.i.lock().unwrap() += 1; - } - } - let _u = Unwinder { i: arc2 }; - panic!(); - }) - .join(); - let lock = arc.lock().unwrap(); - assert_eq!(*lock, 2); -} - #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] fn panic_while_mapping_unlocked_poison() { From 64a27c2e370e1f9e50fb231fc7d6a4debcebe985 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 29 Jul 2025 09:46:41 +0000 Subject: [PATCH 167/201] resuse eagerly resolved goal from previous iteration --- .../src/solve/eval_ctxt/canonical.rs | 9 ++---- .../src/solve/eval_ctxt/mod.rs | 31 +++++++++++++++---- .../rustc_next_trait_solver/src/solve/mod.rs | 4 +++ .../src/solve/fulfill.rs | 6 +++- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index de1330ca82a4..74c5b49ea92f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -53,17 +53,14 @@ where { /// Canonicalizes the goal remembering the original values /// for each bound variable. + /// + /// This expects `goal` and `opaque_types` to be eager resolved. pub(super) fn canonicalize_goal( &self, is_hir_typeck_root_goal: bool, goal: Goal, + opaque_types: Vec<(ty::OpaqueTypeKey, I::Ty)>, ) -> (Vec, CanonicalInput) { - // We only care about one entry per `OpaqueTypeKey` here, - // so we only canonicalize the lookup table and ignore - // duplicate entries. - let opaque_types = self.delegate.clone_opaque_types_lookup_table(); - let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); - let mut orig_values = Default::default(); let canonical = Canonicalizer::canonicalize_input( self.delegate, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 053ccf285cf0..8671cc7c3d30 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -20,6 +20,7 @@ use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::placeholder::BoundVarReplacer; +use crate::resolve::eager_resolve_vars; use crate::solve::inspect::{self, ProofTreeBuilder}; use crate::solve::search_graph::SearchGraph; use crate::solve::ty::may_use_unstable_feature; @@ -440,6 +441,7 @@ where return Ok(( NestedNormalizationGoals::empty(), GoalEvaluation { + goal, certainty: Certainty::Maybe(stalled_on.stalled_cause), has_changed: HasChanged::No, stalled_on: Some(stalled_on), @@ -447,10 +449,16 @@ where )); } + // We only care about one entry per `OpaqueTypeKey` here, + // so we only canonicalize the lookup table and ignore + // duplicate entries. + let opaque_types = self.delegate.clone_opaque_types_lookup_table(); + let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types)); + let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root) && self.delegate.in_hir_typeck(); - - let (orig_values, canonical_goal) = self.canonicalize_goal(is_hir_typeck_root_goal, goal); + let (orig_values, canonical_goal) = + self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind); let canonical_result = self.search_graph.evaluate_goal( @@ -528,7 +536,10 @@ where }, }; - Ok((normalization_nested_goals, GoalEvaluation { certainty, has_changed, stalled_on })) + Ok(( + normalization_nested_goals, + GoalEvaluation { goal, certainty, has_changed, stalled_on }, + )) } pub(super) fn compute_goal(&mut self, goal: Goal) -> QueryResult { @@ -664,7 +675,7 @@ where let ( NestedNormalizationGoals(nested_goals), - GoalEvaluation { certainty, stalled_on, has_changed: _ }, + GoalEvaluation { goal, certainty, stalled_on, has_changed: _ }, ) = self.evaluate_goal_raw( GoalEvaluationKind::Nested, source, @@ -702,7 +713,15 @@ where // FIXME: Do we need to eagerly resolve here? Or should we check // if the cache key has any changed vars? let with_resolved_vars = self.resolve_vars_if_possible(goal); - if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias { + if pred.alias + != with_resolved_vars + .predicate + .as_normalizes_to() + .unwrap() + .no_bound_vars() + .unwrap() + .alias + { unchanged_certainty = None; } @@ -714,7 +733,7 @@ where } } } else { - let GoalEvaluation { certainty, has_changed, stalled_on } = + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = self.evaluate_goal(GoalEvaluationKind::Nested, source, goal, stalled_on)?; if has_changed == HasChanged::Yes { unchanged_certainty = None; diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index f39426c7689d..9d52dee3c659 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -386,6 +386,10 @@ fn response_no_constraints_raw( /// The result of evaluating a goal. pub struct GoalEvaluation { + /// The goal we've evaluated. This is the input goal, but potentially with its + /// inference variables resolved. This never applies any inference constraints + /// from evaluating the goal. + pub goal: Goal, pub certainty: Certainty, pub has_changed: HasChanged, /// If the [`Certainty`] was `Maybe`, then keep track of whether the goal has changed diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 3ce0f0255124..70452947a8b5 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -207,7 +207,7 @@ where let result = delegate.evaluate_root_goal(goal, obligation.cause.span, stalled_on); self.inspect_evaluated_obligation(infcx, &obligation, &result); - let GoalEvaluation { certainty, has_changed, stalled_on } = match result { + let GoalEvaluation { goal, certainty, has_changed, stalled_on } = match result { Ok(result) => result, Err(NoSolution) => { errors.push(E::from_solver_error( @@ -218,6 +218,10 @@ where } }; + // We've resolved the goal in `evaluate_root_goal`, avoid redoing this work + // in the next iteration. This does not resolve the inference variables + // constrained by evaluating the goal. + obligation.predicate = goal.predicate; if has_changed == HasChanged::Yes { // We increment the recursion depth here to track the number of times // this goal has resulted in inference progress. This doesn't precisely From 27610341768951b2d46071683bcb87ea7c690e98 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 28 Jul 2025 12:05:17 +1000 Subject: [PATCH 168/201] coverage: Test how `#[automatically_derived]` affects instrumentation --- tests/coverage/auto-derived.auto.cov-map | 47 +++++++++++++++++ tests/coverage/auto-derived.auto.coverage | 54 ++++++++++++++++++++ tests/coverage/auto-derived.base.cov-map | 61 +++++++++++++++++++++++ tests/coverage/auto-derived.base.coverage | 54 ++++++++++++++++++++ tests/coverage/auto-derived.on.cov-map | 47 +++++++++++++++++ tests/coverage/auto-derived.on.coverage | 54 ++++++++++++++++++++ tests/coverage/auto-derived.rs | 53 ++++++++++++++++++++ 7 files changed, 370 insertions(+) create mode 100644 tests/coverage/auto-derived.auto.cov-map create mode 100644 tests/coverage/auto-derived.auto.coverage create mode 100644 tests/coverage/auto-derived.base.cov-map create mode 100644 tests/coverage/auto-derived.base.coverage create mode 100644 tests/coverage/auto-derived.on.cov-map create mode 100644 tests/coverage/auto-derived.on.coverage create mode 100644 tests/coverage/auto-derived.rs diff --git a/tests/coverage/auto-derived.auto.cov-map b/tests/coverage/auto-derived.auto.cov-map new file mode 100644 index 000000000000..a0aa1206de42 --- /dev/null +++ b/tests/coverage/auto-derived.auto.cov-map @@ -0,0 +1,47 @@ +Function name: ::my_assoc_fn::inner_fn +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::inner_fn_on +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::{closure#0} +Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: auto_derived::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/auto-derived.auto.coverage b/tests/coverage/auto-derived.auto.coverage new file mode 100644 index 000000000000..960bd11ec3a1 --- /dev/null +++ b/tests/coverage/auto-derived.auto.coverage @@ -0,0 +1,54 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2024 + LL| |//@ revisions: base auto on + LL| | + LL| |// Tests for how `#[automatically_derived]` affects coverage instrumentation. + LL| |// + LL| |// The actual behaviour is an implementation detail, so this test mostly exists + LL| |// to show when that behaviour has been accidentally or deliberately changed. + LL| |// + LL| |// Revision guide: + LL| |// - base: Test baseline instrumentation behaviour without `#[automatically_derived]` + LL| |// - auto: Test how `#[automatically_derived]` affects instrumentation + LL| |// - on: Test interaction between auto-derived and `#[coverage(on)]` + LL| | + LL| |struct MyStruct; + LL| | + LL| |trait MyTrait { + LL| | fn my_assoc_fn(); + LL| |} + LL| | + LL| |#[cfg_attr(auto, automatically_derived)] + LL| |#[cfg_attr(on, automatically_derived)] + LL| |#[cfg_attr(on, coverage(on))] + LL| |impl MyTrait for MyStruct { + LL| | fn my_assoc_fn() { + LL| 1| fn inner_fn() { + LL| 1| say("in inner fn"); + LL| 1| } + LL| | + LL| | #[coverage(on)] + LL| 1| fn inner_fn_on() { + LL| 1| say("in inner fn (on)"); + LL| 1| } + LL| | + LL| 1| let closure = || { + LL| 1| say("in closure"); + LL| 1| }; + LL| | + LL| | closure(); + LL| | inner_fn(); + LL| | inner_fn_on(); + LL| | } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |#[inline(never)] + LL| |fn say(s: &str) { + LL| | println!("{s}"); + LL| |} + LL| | + LL| 1|fn main() { + LL| 1| MyStruct::my_assoc_fn(); + LL| 1|} + diff --git a/tests/coverage/auto-derived.base.cov-map b/tests/coverage/auto-derived.base.cov-map new file mode 100644 index 000000000000..2dcd616300d9 --- /dev/null +++ b/tests/coverage/auto-derived.base.cov-map @@ -0,0 +1,61 @@ +Function name: ::my_assoc_fn +Raw bytes (34): 0x[01, 01, 00, 06, 01, 19, 05, 00, 15, 01, 0a, 0d, 00, 14, 01, 04, 09, 00, 12, 01, 01, 09, 00, 11, 01, 01, 09, 00, 14, 01, 01, 05, 00, 06] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 25, 5) to (start + 0, 21) +- Code(Counter(0)) at (prev + 10, 13) to (start + 0, 20) +- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 18) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::inner_fn +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::inner_fn_on +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::{closure#0} +Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: auto_derived::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/auto-derived.base.coverage b/tests/coverage/auto-derived.base.coverage new file mode 100644 index 000000000000..6ab6aa5c4dfc --- /dev/null +++ b/tests/coverage/auto-derived.base.coverage @@ -0,0 +1,54 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2024 + LL| |//@ revisions: base auto on + LL| | + LL| |// Tests for how `#[automatically_derived]` affects coverage instrumentation. + LL| |// + LL| |// The actual behaviour is an implementation detail, so this test mostly exists + LL| |// to show when that behaviour has been accidentally or deliberately changed. + LL| |// + LL| |// Revision guide: + LL| |// - base: Test baseline instrumentation behaviour without `#[automatically_derived]` + LL| |// - auto: Test how `#[automatically_derived]` affects instrumentation + LL| |// - on: Test interaction between auto-derived and `#[coverage(on)]` + LL| | + LL| |struct MyStruct; + LL| | + LL| |trait MyTrait { + LL| | fn my_assoc_fn(); + LL| |} + LL| | + LL| |#[cfg_attr(auto, automatically_derived)] + LL| |#[cfg_attr(on, automatically_derived)] + LL| |#[cfg_attr(on, coverage(on))] + LL| |impl MyTrait for MyStruct { + LL| 1| fn my_assoc_fn() { + LL| 1| fn inner_fn() { + LL| 1| say("in inner fn"); + LL| 1| } + LL| | + LL| | #[coverage(on)] + LL| 1| fn inner_fn_on() { + LL| 1| say("in inner fn (on)"); + LL| 1| } + LL| | + LL| 1| let closure = || { + LL| 1| say("in closure"); + LL| 1| }; + LL| | + LL| 1| closure(); + LL| 1| inner_fn(); + LL| 1| inner_fn_on(); + LL| 1| } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |#[inline(never)] + LL| |fn say(s: &str) { + LL| | println!("{s}"); + LL| |} + LL| | + LL| 1|fn main() { + LL| 1| MyStruct::my_assoc_fn(); + LL| 1|} + diff --git a/tests/coverage/auto-derived.on.cov-map b/tests/coverage/auto-derived.on.cov-map new file mode 100644 index 000000000000..a0aa1206de42 --- /dev/null +++ b/tests/coverage/auto-derived.on.cov-map @@ -0,0 +1,47 @@ +Function name: ::my_assoc_fn::inner_fn +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::inner_fn_on +Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 31, 9) to (start + 0, 25) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 35) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: ::my_assoc_fn::{closure#0} +Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 4 +- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27) +- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) +- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) +Highest counter ID seen: c0 + +Function name: auto_derived::main +Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 3 +- Code(Counter(0)) at (prev + 51, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 26) +- Code(Counter(0)) at (prev + 1, 1) to (start + 0, 2) +Highest counter ID seen: c0 + diff --git a/tests/coverage/auto-derived.on.coverage b/tests/coverage/auto-derived.on.coverage new file mode 100644 index 000000000000..960bd11ec3a1 --- /dev/null +++ b/tests/coverage/auto-derived.on.coverage @@ -0,0 +1,54 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2024 + LL| |//@ revisions: base auto on + LL| | + LL| |// Tests for how `#[automatically_derived]` affects coverage instrumentation. + LL| |// + LL| |// The actual behaviour is an implementation detail, so this test mostly exists + LL| |// to show when that behaviour has been accidentally or deliberately changed. + LL| |// + LL| |// Revision guide: + LL| |// - base: Test baseline instrumentation behaviour without `#[automatically_derived]` + LL| |// - auto: Test how `#[automatically_derived]` affects instrumentation + LL| |// - on: Test interaction between auto-derived and `#[coverage(on)]` + LL| | + LL| |struct MyStruct; + LL| | + LL| |trait MyTrait { + LL| | fn my_assoc_fn(); + LL| |} + LL| | + LL| |#[cfg_attr(auto, automatically_derived)] + LL| |#[cfg_attr(on, automatically_derived)] + LL| |#[cfg_attr(on, coverage(on))] + LL| |impl MyTrait for MyStruct { + LL| | fn my_assoc_fn() { + LL| 1| fn inner_fn() { + LL| 1| say("in inner fn"); + LL| 1| } + LL| | + LL| | #[coverage(on)] + LL| 1| fn inner_fn_on() { + LL| 1| say("in inner fn (on)"); + LL| 1| } + LL| | + LL| 1| let closure = || { + LL| 1| say("in closure"); + LL| 1| }; + LL| | + LL| | closure(); + LL| | inner_fn(); + LL| | inner_fn_on(); + LL| | } + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |#[inline(never)] + LL| |fn say(s: &str) { + LL| | println!("{s}"); + LL| |} + LL| | + LL| 1|fn main() { + LL| 1| MyStruct::my_assoc_fn(); + LL| 1|} + diff --git a/tests/coverage/auto-derived.rs b/tests/coverage/auto-derived.rs new file mode 100644 index 000000000000..cbf8279918a2 --- /dev/null +++ b/tests/coverage/auto-derived.rs @@ -0,0 +1,53 @@ +#![feature(coverage_attribute)] +//@ edition: 2024 +//@ revisions: base auto on + +// Tests for how `#[automatically_derived]` affects coverage instrumentation. +// +// The actual behaviour is an implementation detail, so this test mostly exists +// to show when that behaviour has been accidentally or deliberately changed. +// +// Revision guide: +// - base: Test baseline instrumentation behaviour without `#[automatically_derived]` +// - auto: Test how `#[automatically_derived]` affects instrumentation +// - on: Test interaction between auto-derived and `#[coverage(on)]` + +struct MyStruct; + +trait MyTrait { + fn my_assoc_fn(); +} + +#[cfg_attr(auto, automatically_derived)] +#[cfg_attr(on, automatically_derived)] +#[cfg_attr(on, coverage(on))] +impl MyTrait for MyStruct { + fn my_assoc_fn() { + fn inner_fn() { + say("in inner fn"); + } + + #[coverage(on)] + fn inner_fn_on() { + say("in inner fn (on)"); + } + + let closure = || { + say("in closure"); + }; + + closure(); + inner_fn(); + inner_fn_on(); + } +} + +#[coverage(off)] +#[inline(never)] +fn say(s: &str) { + println!("{s}"); +} + +fn main() { + MyStruct::my_assoc_fn(); +} From b4d0c91635e87be830282cb86d23882b6b6f9dad Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 28 Jul 2025 12:29:40 +1000 Subject: [PATCH 169/201] coverage: Rename `CoverageStatus` to `CoverageAttrKind` This patch also prepares the affected code in `coverage_attr_on` for some subsequent changes. --- .../src/attributes.rs | 20 ++++--------- .../src/attributes/codegen_attrs.rs | 10 +++---- .../rustc_mir_transform/src/coverage/query.rs | 29 ++++++++++--------- 3 files changed, 27 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs index 610c93a253ce..55019cd57a71 100644 --- a/compiler/rustc_attr_data_structures/src/attributes.rs +++ b/compiler/rustc_attr_data_structures/src/attributes.rs @@ -110,18 +110,10 @@ pub enum DeprecatedSince { Err, } -#[derive( - Copy, - Debug, - Eq, - PartialEq, - Encodable, - Decodable, - Clone, - HashStable_Generic, - PrintAttribute -)] -pub enum CoverageStatus { +/// Successfully-parsed value of a `#[coverage(..)]` attribute. +#[derive(Copy, Debug, Eq, PartialEq, Encodable, Decodable, Clone)] +#[derive(HashStable_Generic, PrintAttribute)] +pub enum CoverageAttrKind { On, Off, } @@ -304,8 +296,8 @@ pub enum AttributeKind { /// Represents `#[const_trait]`. ConstTrait(Span), - /// Represents `#[coverage]`. - Coverage(Span, CoverageStatus), + /// Represents `#[coverage(..)]`. + Coverage(Span, CoverageAttrKind), ///Represents `#[rustc_deny_explicit_impl]`. DenyExplicitImpl(Span), diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs index bb28121c2c5d..afa9abed0bb5 100644 --- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, CoverageStatus, OptimizeAttr, UsedBy}; +use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy}; use rustc_feature::{AttributeTemplate, template}; use rustc_session::parse::feature_err; use rustc_span::{Span, Symbol, sym}; @@ -78,16 +78,16 @@ impl SingleAttributeParser for CoverageParser { return None; }; - let status = match arg.path().word_sym() { - Some(sym::off) => CoverageStatus::Off, - Some(sym::on) => CoverageStatus::On, + let kind = match arg.path().word_sym() { + Some(sym::off) => CoverageAttrKind::Off, + Some(sym::on) => CoverageAttrKind::On, None | Some(_) => { fail_incorrect_argument(arg.span()); return None; } }; - Some(AttributeKind::Coverage(cx.attr_span, status)) + Some(AttributeKind::Coverage(cx.attr_span, kind)) } } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 551f720c869e..02e433e53c92 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,4 +1,4 @@ -use rustc_attr_data_structures::{AttributeKind, CoverageStatus, find_attr}; +use rustc_attr_data_structures::{AttributeKind, CoverageAttrKind, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{BasicCoverageBlock, CoverageIdsInfo, CoverageKind, MappingKind}; @@ -57,20 +57,23 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { /// Query implementation for `coverage_attr_on`. fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - // Check for annotations directly on this def. - if let Some(coverage_status) = - find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_, status) => status) + // Check for a `#[coverage(..)]` attribute on this def. + if let Some(kind) = + find_attr!(tcx.get_all_attrs(def_id), AttributeKind::Coverage(_sp, kind) => kind) { - *coverage_status == CoverageStatus::On - } else { - match tcx.opt_local_parent(def_id) { - // Check the parent def (and so on recursively) until we find an - // enclosing attribute or reach the crate root. - Some(parent) => tcx.coverage_attr_on(parent), - // We reached the crate root without seeing a coverage attribute, so - // allow coverage instrumentation by default. - None => true, + match kind { + CoverageAttrKind::On => return true, + CoverageAttrKind::Off => return false, } + }; + + // Check the parent def (and so on recursively) until we find an + // enclosing attribute or reach the crate root. + match tcx.opt_local_parent(def_id) { + Some(parent) => tcx.coverage_attr_on(parent), + // We reached the crate root without seeing a coverage attribute, so + // allow coverage instrumentation by default. + None => true, } } From 682f744f89e1c014e91b2fd7058c75f2056ac870 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 28 Jul 2025 12:25:05 +1000 Subject: [PATCH 170/201] coverage: Treat `#[automatically_derived]` as `#[coverage(off)]` --- .../rustc_mir_transform/src/coverage/query.rs | 19 +++++++-------- tests/coverage/auto-derived.auto.cov-map | 24 ------------------- tests/coverage/auto-derived.auto.coverage | 12 +++++----- tests/coverage/auto-derived.on.cov-map | 14 +++++++++++ tests/coverage/auto-derived.on.coverage | 10 ++++---- 5 files changed, 34 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 02e433e53c92..73795e47d2e9 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -32,16 +32,6 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { return false; } - // Don't instrument functions with `#[automatically_derived]` on their - // enclosing impl block, on the assumption that most users won't care about - // coverage for derived impls. - if let Some(impl_of) = tcx.impl_of_assoc(def_id.to_def_id()) - && tcx.is_automatically_derived(impl_of) - { - trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); - return false; - } - if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) { trace!("InstrumentCoverage skipped for {def_id:?} (`#[naked]`)"); return false; @@ -67,6 +57,15 @@ fn coverage_attr_on(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { } }; + // Treat `#[automatically_derived]` as an implied `#[coverage(off)]`, on + // the assumption that most users won't want coverage for derived impls. + // + // This affects not just the associated items of an impl block, but also + // any closures and other nested functions within those associated items. + if tcx.is_automatically_derived(def_id.to_def_id()) { + return false; + } + // Check the parent def (and so on recursively) until we find an // enclosing attribute or reach the crate root. match tcx.opt_local_parent(def_id) { diff --git a/tests/coverage/auto-derived.auto.cov-map b/tests/coverage/auto-derived.auto.cov-map index a0aa1206de42..e3d411d895aa 100644 --- a/tests/coverage/auto-derived.auto.cov-map +++ b/tests/coverage/auto-derived.auto.cov-map @@ -1,15 +1,3 @@ -Function name: ::my_assoc_fn::inner_fn -Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a] -Number of files: 1 -- file 0 => $DIR/auto-derived.rs -Number of expressions: 0 -Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 26, 9) to (start + 0, 22) -- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) -- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 30) -- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) -Highest counter ID seen: c0 - Function name: ::my_assoc_fn::inner_fn_on Raw bytes (24): 0x[01, 01, 00, 04, 01, 1f, 09, 00, 19, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 23, 01, 01, 09, 00, 0a] Number of files: 1 @@ -22,18 +10,6 @@ Number of file 0 mappings: 4 - Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) Highest counter ID seen: c0 -Function name: ::my_assoc_fn::{closure#0} -Raw bytes (24): 0x[01, 01, 00, 04, 01, 23, 1a, 00, 1b, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1d, 01, 01, 09, 00, 0a] -Number of files: 1 -- file 0 => $DIR/auto-derived.rs -Number of expressions: 0 -Number of file 0 mappings: 4 -- Code(Counter(0)) at (prev + 35, 26) to (start + 0, 27) -- Code(Counter(0)) at (prev + 1, 13) to (start + 0, 16) -- Code(Counter(0)) at (prev + 0, 17) to (start + 0, 29) -- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 10) -Highest counter ID seen: c0 - Function name: auto_derived::main Raw bytes (19): 0x[01, 01, 00, 03, 01, 33, 01, 00, 0a, 01, 01, 05, 00, 1a, 01, 01, 01, 00, 02] Number of files: 1 diff --git a/tests/coverage/auto-derived.auto.coverage b/tests/coverage/auto-derived.auto.coverage index 960bd11ec3a1..242abbf80318 100644 --- a/tests/coverage/auto-derived.auto.coverage +++ b/tests/coverage/auto-derived.auto.coverage @@ -23,18 +23,18 @@ LL| |#[cfg_attr(on, coverage(on))] LL| |impl MyTrait for MyStruct { LL| | fn my_assoc_fn() { - LL| 1| fn inner_fn() { - LL| 1| say("in inner fn"); - LL| 1| } + LL| | fn inner_fn() { + LL| | say("in inner fn"); + LL| | } LL| | LL| | #[coverage(on)] LL| 1| fn inner_fn_on() { LL| 1| say("in inner fn (on)"); LL| 1| } LL| | - LL| 1| let closure = || { - LL| 1| say("in closure"); - LL| 1| }; + LL| | let closure = || { + LL| | say("in closure"); + LL| | }; LL| | LL| | closure(); LL| | inner_fn(); diff --git a/tests/coverage/auto-derived.on.cov-map b/tests/coverage/auto-derived.on.cov-map index a0aa1206de42..2dcd616300d9 100644 --- a/tests/coverage/auto-derived.on.cov-map +++ b/tests/coverage/auto-derived.on.cov-map @@ -1,3 +1,17 @@ +Function name: ::my_assoc_fn +Raw bytes (34): 0x[01, 01, 00, 06, 01, 19, 05, 00, 15, 01, 0a, 0d, 00, 14, 01, 04, 09, 00, 12, 01, 01, 09, 00, 11, 01, 01, 09, 00, 14, 01, 01, 05, 00, 06] +Number of files: 1 +- file 0 => $DIR/auto-derived.rs +Number of expressions: 0 +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 25, 5) to (start + 0, 21) +- Code(Counter(0)) at (prev + 10, 13) to (start + 0, 20) +- Code(Counter(0)) at (prev + 4, 9) to (start + 0, 18) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 17) +- Code(Counter(0)) at (prev + 1, 9) to (start + 0, 20) +- Code(Counter(0)) at (prev + 1, 5) to (start + 0, 6) +Highest counter ID seen: c0 + Function name: ::my_assoc_fn::inner_fn Raw bytes (24): 0x[01, 01, 00, 04, 01, 1a, 09, 00, 16, 01, 01, 0d, 00, 10, 01, 00, 11, 00, 1e, 01, 01, 09, 00, 0a] Number of files: 1 diff --git a/tests/coverage/auto-derived.on.coverage b/tests/coverage/auto-derived.on.coverage index 960bd11ec3a1..6ab6aa5c4dfc 100644 --- a/tests/coverage/auto-derived.on.coverage +++ b/tests/coverage/auto-derived.on.coverage @@ -22,7 +22,7 @@ LL| |#[cfg_attr(on, automatically_derived)] LL| |#[cfg_attr(on, coverage(on))] LL| |impl MyTrait for MyStruct { - LL| | fn my_assoc_fn() { + LL| 1| fn my_assoc_fn() { LL| 1| fn inner_fn() { LL| 1| say("in inner fn"); LL| 1| } @@ -36,10 +36,10 @@ LL| 1| say("in closure"); LL| 1| }; LL| | - LL| | closure(); - LL| | inner_fn(); - LL| | inner_fn_on(); - LL| | } + LL| 1| closure(); + LL| 1| inner_fn(); + LL| 1| inner_fn_on(); + LL| 1| } LL| |} LL| | LL| |#[coverage(off)] From 56d9ed74452c8e19c532e98294d5d01925822e94 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 22 Jul 2025 14:18:46 +0200 Subject: [PATCH 171/201] Fix nvptx-safe-naming.rs test on LLVM 21 This is now printed on the same line. Use NEXT/SAME depending on the LLVM version. --- tests/assembly-llvm/nvptx-safe-naming.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/assembly-llvm/nvptx-safe-naming.rs b/tests/assembly-llvm/nvptx-safe-naming.rs index d7b46aadd9cc..6a6659a4e304 100644 --- a/tests/assembly-llvm/nvptx-safe-naming.rs +++ b/tests/assembly-llvm/nvptx-safe-naming.rs @@ -1,6 +1,9 @@ //@ assembly-output: ptx-linker //@ compile-flags: --crate-type cdylib -Z unstable-options -Clinker-flavor=llbc //@ only-nvptx64 +//@ revisions: LLVM20 LLVM21 +//@ [LLVM21] min-llvm-version: 21 +//@ [LLVM20] max-llvm-major-version: 20 #![feature(abi_ptx)] #![no_std] @@ -15,7 +18,8 @@ extern crate breakpoint_panic_handler; #[no_mangle] pub unsafe extern "ptx-kernel" fn top_kernel(a: *const u32, b: *mut u32) { // CHECK: call.uni (retval0), - // CHECK-NEXT: [[IMPL_FN]] + // LLVM20-NEXT: [[IMPL_FN]] + // LLVM21-SAME: [[IMPL_FN]] *b = deep::private::MyStruct::new(*a).square(); } From d50b4f10f18b2f84be72692e39dd5de7e8dde949 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 Jul 2025 12:05:36 +0200 Subject: [PATCH 172/201] Adjust enum-discriminant-eq.rs for LLVM 21 The two xors get folded into the select. --- tests/codegen-llvm/enum/enum-discriminant-eq.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/codegen-llvm/enum/enum-discriminant-eq.rs b/tests/codegen-llvm/enum/enum-discriminant-eq.rs index 0494c5f551b3..d599685c2e51 100644 --- a/tests/codegen-llvm/enum/enum-discriminant-eq.rs +++ b/tests/codegen-llvm/enum/enum-discriminant-eq.rs @@ -1,6 +1,9 @@ //@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled //@ min-llvm-version: 20 //@ only-64bit +//@ revisions: LLVM20 LLVM21 +//@ [LLVM21] min-llvm-version: 21 +//@ [LLVM20] max-llvm-major-version: 20 // The `derive(PartialEq)` on enums with field-less variants compares discriminants, // so make sure we emit that in some reasonable way. @@ -137,17 +140,20 @@ pub fn mid_nz32_eq_discr(a: Mid>, b: Mid>) -> bool { pub fn mid_ac_eq_discr(a: Mid, b: Mid) -> bool { // CHECK-LABEL: @mid_ac_eq_discr( - // CHECK: %[[A_REL_DISCR:.+]] = xor i8 %a, -128 + // LLVM20: %[[A_REL_DISCR:.+]] = xor i8 %a, -128 // CHECK: %[[A_IS_NICHE:.+]] = icmp slt i8 %a, 0 // CHECK: %[[A_NOT_HOLE:.+]] = icmp ne i8 %a, -127 // CHECK: tail call void @llvm.assume(i1 %[[A_NOT_HOLE]]) - // CHECK: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %[[A_REL_DISCR]], i8 1 + // LLVM20: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %[[A_REL_DISCR]], i8 1 - // CHECK: %[[B_REL_DISCR:.+]] = xor i8 %b, -128 + // LLVM20: %[[B_REL_DISCR:.+]] = xor i8 %b, -128 // CHECK: %[[B_IS_NICHE:.+]] = icmp slt i8 %b, 0 // CHECK: %[[B_NOT_HOLE:.+]] = icmp ne i8 %b, -127 // CHECK: tail call void @llvm.assume(i1 %[[B_NOT_HOLE]]) - // CHECK: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %[[B_REL_DISCR]], i8 1 + // LLVM20: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %[[B_REL_DISCR]], i8 1 + + // LLVM21: %[[A_DISCR:.+]] = select i1 %[[A_IS_NICHE]], i8 %a, i8 -127 + // LLVM21: %[[B_DISCR:.+]] = select i1 %[[B_IS_NICHE]], i8 %b, i8 -127 // CHECK: %[[R:.+]] = icmp eq i8 %[[A_DISCR]], %[[B_DISCR]] // CHECK: ret i1 %[[R]] From 652b9eb98b3dc5be8b2b65954e252e9e272c3447 Mon Sep 17 00:00:00 2001 From: FractalFir Date: Tue, 29 Jul 2025 12:42:48 +0200 Subject: [PATCH 173/201] Add support for the m68k architecture in 'object_architecture' --- compiler/rustc_target/src/spec/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index c64cd9a51b7d..033590e01a67 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -3598,6 +3598,7 @@ impl Target { ), "x86" => (Architecture::I386, None), "s390x" => (Architecture::S390x, None), + "m68k" => (Architecture::M68k, None), "mips" | "mips32r6" => (Architecture::Mips, None), "mips64" | "mips64r6" => ( // While there are currently no builtin targets From de02a32cf1203a9c9efb97a66dac44b7c646bb24 Mon Sep 17 00:00:00 2001 From: Lucas Werkmeister Date: Tue, 29 Jul 2025 13:24:41 +0200 Subject: [PATCH 174/201] Fix typo in `DropGuard` doc --- library/core/src/mem/drop_guard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/mem/drop_guard.rs b/library/core/src/mem/drop_guard.rs index 47ccb69acc80..fecc94b815e9 100644 --- a/library/core/src/mem/drop_guard.rs +++ b/library/core/src/mem/drop_guard.rs @@ -4,7 +4,7 @@ use crate::ops::{Deref, DerefMut}; /// Wrap a value and run a closure when dropped. /// -/// This is useful for quickly creating desructors inline. +/// This is useful for quickly creating destructors inline. /// /// # Examples /// From ccf660f855eec84f976ed07190d9e8a8b93e2c1f Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 29 Jul 2025 14:07:27 +0200 Subject: [PATCH 175/201] Relax check lines in x86-return-float.rs On LLVM 21 additional %esp adjustments are generated. Don't use NEXT to allow these. --- tests/assembly-llvm/x86-return-float.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/assembly-llvm/x86-return-float.rs b/tests/assembly-llvm/x86-return-float.rs index 165c11d22801..4db93f68485a 100644 --- a/tests/assembly-llvm/x86-return-float.rs +++ b/tests/assembly-llvm/x86-return-float.rs @@ -334,9 +334,9 @@ pub fn return_f128(x: f128) -> f128 { // linux-NEXT: .cfi_offset // CHECK-NEXT: movl %esp, %ebp // linux-NEXT: .cfi_def_cfa_register - // linux-NEXT: movaps 8(%ebp), %xmm0 - // win-NEXT: movups 8(%ebp), %xmm0 - // CHECK-NEXT: popl %ebp + // linux: movaps 8(%ebp), %xmm0 + // win: movups 8(%ebp), %xmm0 + // CHECK: popl %ebp // linux-NEXT: .cfi_def_cfa // CHECK-NEXT: retl x From 1ceacf55a0e207c08bbedfea422c0842a4983e3d Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 24 Jun 2025 20:19:10 +0800 Subject: [PATCH 176/201] LoongArch64 LSX fast-path for `str.contains(&str)` Benchmark results with LLVM 21 on LA664: ``` OLD: test bench_is_contained_in ... bench: 43.63 ns/iter (+/- 0.04) NEW: test bench_is_contained_in ... bench: 12.81 ns/iter (+/- 0.01) ``` --- library/core/src/str/pattern.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/library/core/src/str/pattern.rs b/library/core/src/str/pattern.rs index bcbbb11c83b2..e116b1383832 100644 --- a/library/core/src/str/pattern.rs +++ b/library/core/src/str/pattern.rs @@ -996,7 +996,10 @@ impl<'b> Pattern for &'b str { return haystack.as_bytes().contains(&self.as_bytes()[0]); } - #[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] + #[cfg(any( + all(target_arch = "x86_64", target_feature = "sse2"), + all(target_arch = "loongarch64", target_feature = "lsx") + ))] if self.len() <= 32 { if let Some(result) = simd_contains(self, haystack) { return result; @@ -1770,11 +1773,18 @@ impl TwoWayStrategy for RejectAndMatch { /// If we ever ship std with for x86-64-v3 or adapt this for other platforms then wider vectors /// should be evaluated. /// +/// Similarly, on LoongArch the 128-bit LSX vector extension is the baseline, +/// so we also use `u8x16` there. Wider vector widths may be considered +/// for future LoongArch extensions (e.g., LASX). +/// /// For haystacks smaller than vector-size + needle length it falls back to /// a naive O(n*m) search so this implementation should not be called on larger needles. /// /// [0]: http://0x80.pl/articles/simd-strfind.html#sse-avx2 -#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] +#[cfg(any( + all(target_arch = "x86_64", target_feature = "sse2"), + all(target_arch = "loongarch64", target_feature = "lsx") +))] #[inline] fn simd_contains(needle: &str, haystack: &str) -> Option { let needle = needle.as_bytes(); @@ -1906,7 +1916,10 @@ fn simd_contains(needle: &str, haystack: &str) -> Option { /// # Safety /// /// Both slices must have the same length. -#[cfg(all(target_arch = "x86_64", target_feature = "sse2"))] // only called on x86 +#[cfg(any( + all(target_arch = "x86_64", target_feature = "sse2"), + all(target_arch = "loongarch64", target_feature = "lsx") +))] #[inline] unsafe fn small_slice_eq(x: &[u8], y: &[u8]) -> bool { debug_assert_eq!(x.len(), y.len()); From c39b4479ce6f33604000b9ffdc91d2e8e115cb41 Mon Sep 17 00:00:00 2001 From: LorrensP-2158466 Date: Mon, 28 Jul 2025 21:21:59 +0200 Subject: [PATCH 177/201] "Cachify" `ExternPreludeEntry.binding` through a `Cell`. --- compiler/rustc_resolve/src/build_reduced_graph.rs | 11 +++++------ compiler/rustc_resolve/src/lib.rs | 12 ++++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index b15b478a6be9..7912345ec56a 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -984,18 +984,17 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { // more details: https://github.com/rust-lang/rust/pull/111761 return; } - let entry = self - .r - .extern_prelude - .entry(ident) - .or_insert(ExternPreludeEntry { binding: None, introduced_by_item: true }); + let entry = self.r.extern_prelude.entry(ident).or_insert(ExternPreludeEntry { + binding: Cell::new(None), + introduced_by_item: true, + }); if orig_name.is_some() { entry.introduced_by_item = true; } // Binding from `extern crate` item in source code can replace // a binding from `--extern` on command line here. if !entry.is_import() { - entry.binding = Some(imported_binding) + entry.binding.set(Some(imported_binding)); } else if ident.name != kw::Underscore { self.r.dcx().span_delayed_bug( item.span, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 8115b87dcae0..88dfb50b47dd 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1009,13 +1009,13 @@ impl<'ra> NameBindingData<'ra> { #[derive(Default, Clone)] struct ExternPreludeEntry<'ra> { - binding: Option>, + binding: Cell>>, introduced_by_item: bool, } impl ExternPreludeEntry<'_> { fn is_import(&self) -> bool { - self.binding.is_some_and(|binding| binding.is_import()) + self.binding.get().is_some_and(|binding| binding.is_import()) } } @@ -2006,7 +2006,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // but not introduce it, as used if they are accessed from lexical scope. if used == Used::Scope { if let Some(entry) = self.extern_prelude.get(&ident.normalize_to_macros_2_0()) { - if !entry.introduced_by_item && entry.binding == Some(used_binding) { + if !entry.introduced_by_item && entry.binding.get() == Some(used_binding) { return; } } @@ -2170,7 +2170,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let norm_ident = ident.normalize_to_macros_2_0(); let binding = self.extern_prelude.get(&norm_ident).cloned().and_then(|entry| { - Some(if let Some(binding) = entry.binding { + Some(if let Some(binding) = entry.binding.get() { if finalize { if !entry.is_import() { self.cstore_mut().process_path_extern(self.tcx, ident.name, ident.span); @@ -2195,8 +2195,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }) }); - if let Some(entry) = self.extern_prelude.get_mut(&norm_ident) { - entry.binding = binding; + if let Some(entry) = self.extern_prelude.get(&norm_ident) { + entry.binding.set(binding); } binding From 75bdbf25e39f073b35eadedcf575affac7762a86 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Thu, 24 Jul 2025 10:19:20 +0000 Subject: [PATCH 178/201] Pick the largest niche even if the largest niche is wrapped around --- compiler/rustc_abi/src/layout.rs | 82 +++++++++++++------ compiler/rustc_abi/src/lib.rs | 13 +++ compiler/rustc_middle/src/ty/layout.rs | 4 +- .../enum-discriminant/wrapping_niche.stderr | 15 +++- .../enums/niche_optimization.rs | 12 +-- 5 files changed, 91 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 716bb716cdb5..90c63bc9db3c 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::fmt::{self, Write}; use std::ops::{Bound, Deref}; use std::{cmp, iter}; @@ -5,7 +6,7 @@ use std::{cmp, iter}; use rustc_hashes::Hash64; use rustc_index::Idx; use rustc_index::bit_set::BitMatrix; -use tracing::debug; +use tracing::{debug, trace}; use crate::{ AbiAlign, Align, BackendRepr, FieldsShape, HasDataLayout, IndexSlice, IndexVec, Integer, @@ -766,30 +767,63 @@ impl LayoutCalculator { let niche_filling_layout = calculate_niche_filling_layout(); - let (mut min, mut max) = (i128::MAX, i128::MIN); let discr_type = repr.discr_type(); - let bits = Integer::from_attr(dl, discr_type).size().bits(); - for (i, mut val) in discriminants { - if !repr.c() && variants[i].iter().any(|f| f.is_uninhabited()) { - continue; - } - if discr_type.is_signed() { - // sign extend the raw representation to be an i128 - val = (val << (128 - bits)) >> (128 - bits); - } - if val < min { - min = val; - } - if val > max { - max = val; - } - } - // We might have no inhabited variants, so pretend there's at least one. - if (min, max) == (i128::MAX, i128::MIN) { - min = 0; - max = 0; - } - assert!(min <= max, "discriminant range is {min}...{max}"); + let discr_int = Integer::from_attr(dl, discr_type); + let bits = discr_int.size().bits(); + // Because we can only represent one range of valid values, we'll look for the + // largest range of invalid values and pick everything else as the range of valid + // values. + + // First we need to sort the possible discriminant values so that we can look for the largest gap: + let valid_discriminants: BTreeSet = discriminants + .filter(|&(i, _)| repr.c() || variants[i].iter().all(|f| !f.is_uninhabited())) + .map(|(_, val)| { + if discr_type.is_signed() { + // sign extend the raw representation to be an i128 + (val << (128 - bits)) >> (128 - bits) + } else { + val + } + }) + .collect(); + trace!(?valid_discriminants); + let discriminants = valid_discriminants.iter().copied(); + //let next_discriminants = discriminants.clone().cycle().skip(1); + let next_discriminants = + discriminants.clone().chain(valid_discriminants.first().copied()).skip(1); + // Iterate over pairs of each discriminant together with the next one. + // Since they were sorted, we can now compute the niche sizes and pick the largest. + let discriminants = discriminants.zip(next_discriminants); + let largest_niche = discriminants.max_by_key(|&(start, end)| { + trace!(?start, ?end); + // If this is a wraparound range, the niche size is `MAX - abs(diff)`, as the diff between + // the two end points is actually the size of the valid discriminants. + let dist = if start > end { + // Overflow can happen for 128 bit discriminants if `end` is negative. + // But in that case casting to `u128` still gets us the right value, + // as the distance must be positive if the lhs of the subtraction is larger than the rhs. + let dist = start.wrapping_sub(end); + if discr_type.is_signed() { + discr_int.signed_max().wrapping_sub(dist) as u128 + } else { + discr_int.size().unsigned_int_max() - dist as u128 + } + } else { + // Overflow can happen for 128 bit discriminants if `start` is negative. + // But in that case casting to `u128` still gets us the right value, + // as the distance must be positive if the lhs of the subtraction is larger than the rhs. + end.wrapping_sub(start) as u128 + }; + trace!(?dist); + dist + }); + trace!(?largest_niche); + + // `max` is the last valid discriminant before the largest niche + // `min` is the first valid discriminant after the largest niche + let (max, min) = largest_niche + // We might have no inhabited variants, so pretend there's at least one. + .unwrap_or((0, 0)); let (min_ity, signed) = discr_range_of_repr(min, max); //Integer::repr_discr(tcx, ty, &repr, min, max); let mut align = dl.aggregate_align; diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index 8e346706877d..14e256b8045d 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1205,6 +1205,19 @@ impl Integer { } } + /// Returns the smallest signed value that can be represented by this Integer. + #[inline] + pub fn signed_min(self) -> i128 { + use Integer::*; + match self { + I8 => i8::MIN as i128, + I16 => i16::MIN as i128, + I32 => i32::MIN as i128, + I64 => i64::MIN as i128, + I128 => i128::MIN, + } + } + /// Finds the smallest Integer type which can represent the signed value. #[inline] pub fn fit_signed(x: i128) -> Integer { diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index a5123576fc64..aed94f9aa04d 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -107,8 +107,8 @@ impl abi::Integer { abi::Integer::I8 }; - // If there are no negative values, we can use the unsigned fit. - if min >= 0 { + // Pick the smallest fit. + if unsigned_fit <= signed_fit { (cmp::max(unsigned_fit, at_least), false) } else { (cmp::max(signed_fit, at_least), true) diff --git a/tests/ui/enum-discriminant/wrapping_niche.stderr b/tests/ui/enum-discriminant/wrapping_niche.stderr index 76cafa43e905..e3e1755e14dd 100644 --- a/tests/ui/enum-discriminant/wrapping_niche.stderr +++ b/tests/ui/enum-discriminant/wrapping_niche.stderr @@ -9,7 +9,7 @@ error: layout_of(UnsignedAroundZero) = Layout { I16, false, ), - valid_range: 0..=65535, + valid_range: (..=1) | (65535..), }, ), fields: Arbitrary { @@ -20,7 +20,16 @@ error: layout_of(UnsignedAroundZero) = Layout { 0, ], }, - largest_niche: None, + largest_niche: Some( + Niche { + offset: Size(0 bytes), + value: Int( + I16, + false, + ), + valid_range: (..=1) | (65535..), + }, + ), uninhabited: false, variants: Multiple { tag: Initialized { @@ -28,7 +37,7 @@ error: layout_of(UnsignedAroundZero) = Layout { I16, false, ), - valid_range: 0..=65535, + valid_range: (..=1) | (65535..), }, tag_encoding: Direct, tag_field: 0, diff --git a/tests/ui/transmutability/enums/niche_optimization.rs b/tests/ui/transmutability/enums/niche_optimization.rs index 2436be500279..316a857662a2 100644 --- a/tests/ui/transmutability/enums/niche_optimization.rs +++ b/tests/ui/transmutability/enums/niche_optimization.rs @@ -75,8 +75,8 @@ fn one_niche() { assert::is_transmutable::(); assert::is_transmutable::(); + assert::is_transmutable::(); assert::is_transmutable::(); - assert::is_transmutable::(); } fn one_niche_alt() { @@ -97,9 +97,9 @@ fn one_niche_alt() { }; assert::is_transmutable::(); - assert::is_transmutable::(); + assert::is_transmutable::(); + assert::is_transmutable::(); assert::is_transmutable::(); - assert::is_transmutable::(); } fn two_niche() { @@ -121,9 +121,9 @@ fn two_niche() { assert::is_transmutable::(); assert::is_transmutable::(); + assert::is_transmutable::(); + assert::is_transmutable::(); assert::is_transmutable::(); - assert::is_transmutable::(); - assert::is_transmutable::(); } fn no_niche() { @@ -142,7 +142,7 @@ fn no_niche() { } const _: () = { - assert!(std::mem::size_of::() == 2); + assert!(std::mem::size_of::() == 1); }; #[repr(C)] From 219bad49461f5c9fc318732a73a05af5d198fed2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 29 Jul 2025 08:43:10 +0000 Subject: [PATCH 179/201] Reuse `sign_extend` helper --- compiler/rustc_abi/src/layout.rs | 4 ++-- compiler/rustc_middle/src/ty/util.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 90c63bc9db3c..c2405553756b 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -769,7 +769,6 @@ impl LayoutCalculator { let discr_type = repr.discr_type(); let discr_int = Integer::from_attr(dl, discr_type); - let bits = discr_int.size().bits(); // Because we can only represent one range of valid values, we'll look for the // largest range of invalid values and pick everything else as the range of valid // values. @@ -780,7 +779,8 @@ impl LayoutCalculator { .map(|(_, val)| { if discr_type.is_signed() { // sign extend the raw representation to be an i128 - (val << (128 - bits)) >> (128 - bits) + // FIXME: do this at the discriminant iterator creation sites + discr_int.size().sign_extend(val as u128) } else { val } diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 174892c6f4d2..a7d07adf78f0 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -32,7 +32,7 @@ use crate::ty::{ #[derive(Copy, Clone, Debug)] pub struct Discr<'tcx> { - /// Bit representation of the discriminant (e.g., `-128i8` is `0xFF_u128`). + /// Bit representation of the discriminant (e.g., `-1i8` is `0xFF_u128`). pub val: u128, pub ty: Ty<'tcx>, } From 0b807fc1afafb52a97f596c3ba3d926aabe6a1ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 16:18:52 +0200 Subject: [PATCH 180/201] Update rustc-perf submodule --- src/tools/rustc-perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index 6a70166b92a1..dde879cf1087 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit 6a70166b92a1b1560cb3cf056427b011b2a1f2bf +Subproject commit dde879cf1087cb34a32287bd8ccc4d545bb9fee5 From d1a877aac268c6bd6cb66f8c7bc5152a080439a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 16:36:48 +0200 Subject: [PATCH 181/201] Improve tidy error on dependency license exceptions --- src/tools/tidy/src/deps.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 8e2a796106f4..b9ad8b1404fc 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -599,7 +599,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) { .other_options(vec!["--locked".to_owned()]); let metadata = t!(cmd.exec()); - check_license_exceptions(&metadata, exceptions, bad); + check_license_exceptions(&metadata, workspace, exceptions, bad); if let Some((crates, permitted_deps)) = permitted_deps { check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad); } @@ -730,14 +730,19 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) { /// Check that all licenses of tool dependencies are in the valid list in `LICENSES`. /// /// Packages listed in `exceptions` are allowed for tools. -fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], bad: &mut bool) { +fn check_license_exceptions( + metadata: &Metadata, + workspace: &str, + exceptions: &[(&str, &str)], + bad: &mut bool, +) { // Validate the EXCEPTIONS list hasn't changed. for (name, license) in exceptions { // Check that the package actually exists. if !metadata.packages.iter().any(|p| *p.name == *name) { tidy_error!( bad, - "could not find exception package `{}`\n\ + "could not find exception package `{}` in workspace `{workspace}`\n\ Remove from EXCEPTIONS list if it is no longer used.", name ); @@ -753,13 +758,15 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba } tidy_error!( bad, - "dependency exception `{}` does not declare a license expression", + "dependency exception `{}` in workspace `{workspace}` does not declare a license expression", pkg.id ); } Some(pkg_license) => { if pkg_license.as_str() != *license { - println!("dependency exception `{name}` license has changed"); + println!( + "dependency exception `{name}` license in workspace `{workspace}` has changed" + ); println!(" previously `{license}` now `{pkg_license}`"); println!(" update EXCEPTIONS for the new license"); *bad = true; @@ -783,12 +790,21 @@ fn check_license_exceptions(metadata: &Metadata, exceptions: &[(&str, &str)], ba let license = match &pkg.license { Some(license) => license, None => { - tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id); + tidy_error!( + bad, + "dependency `{}` in workspace `{workspace}` does not define a license expression", + pkg.id + ); continue; } }; if !LICENSES.contains(&license.as_str()) { - tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id); + tidy_error!( + bad, + "invalid license `{}` for package `{}` in workspace `{workspace}`", + license, + pkg.id + ); } } } From ab4024eb31bf341cb92e1acb1db78e6cab6c434a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 16:41:10 +0200 Subject: [PATCH 182/201] Update license exceptions for rustc-perf --- src/tools/tidy/src/deps.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index b9ad8b1404fc..1ff0349cc1eb 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -167,7 +167,7 @@ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ ("brotli-decompressor", "BSD-3-Clause/MIT"), ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"), ("inferno", "CDDL-1.0"), - ("ring", NON_STANDARD_LICENSE), // see EXCEPTIONS_NON_STANDARD_LICENSE_DEPS for more. + ("option-ext", "MPL-2.0"), ("ryu", "Apache-2.0 OR BSL-1.0"), ("snap", "BSD-3-Clause"), ("subtle", "BSD-3-Clause"), From b60026ab4277a8b37aa0a812baf977b3766520aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 29 Jul 2025 16:41:44 +0200 Subject: [PATCH 183/201] Remove no longer needed handling of nonstandard licenses --- src/tools/tidy/src/deps.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 1ff0349cc1eb..858b058cb7d0 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -226,20 +226,6 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[ ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0 ]; -/// Placeholder for non-standard license file. -const NON_STANDARD_LICENSE: &str = "NON_STANDARD_LICENSE"; - -/// These dependencies have non-standard licenses but are genenrally permitted. -const EXCEPTIONS_NON_STANDARD_LICENSE_DEPS: &[&str] = &[ - // `ring` is included because it is an optional dependency of `hyper`, - // which is a training data in rustc-perf for optimized build. - // The license of it is generally `ISC AND MIT AND OpenSSL`, - // though the `package.license` field is not set. - // - // See https://github.com/briansmith/ring/issues/902 - "ring", -]; - const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!()); /// Crates rustc is allowed to depend on. Avoid adding to the list if possible. @@ -751,11 +737,6 @@ fn check_license_exceptions( for pkg in metadata.packages.iter().filter(|p| *p.name == *name) { match &pkg.license { None => { - if *license == NON_STANDARD_LICENSE - && EXCEPTIONS_NON_STANDARD_LICENSE_DEPS.contains(&pkg.name.as_str()) - { - continue; - } tidy_error!( bad, "dependency exception `{}` in workspace `{workspace}` does not declare a license expression", From 77fc593a6a372d6d8adc3f1b96416f93c3d1aa8d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 28 Jul 2025 07:22:56 -0700 Subject: [PATCH 184/201] Update wasi-sdk to 27.0 in CI This updates the wasi-sdk used in CI to build release binaries and run CI with. No major motivation beyond keeping things up-to-date and following the development of wasi-sdk. --- src/ci/docker/host-x86_64/dist-various-2/Dockerfile | 4 ++-- src/ci/docker/host-x86_64/test-various/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile index e1d83d360872..0855ea222a36 100644 --- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile @@ -81,9 +81,9 @@ RUN /tmp/build-fuchsia-toolchain.sh COPY host-x86_64/dist-various-2/build-x86_64-fortanix-unknown-sgx-toolchain.sh /tmp/ RUN /tmp/build-x86_64-fortanix-unknown-sgx-toolchain.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/tmp/wasi-sdk-25.0-x86_64-linux +ENV WASI_SDK_PATH=/tmp/wasi-sdk-27.0-x86_64-linux COPY scripts/freebsd-toolchain.sh /tmp/ RUN /tmp/freebsd-toolchain.sh i686 diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile index 662a26400ce9..82a820c859dc 100644 --- a/src/ci/docker/host-x86_64/test-various/Dockerfile +++ b/src/ci/docker/host-x86_64/test-various/Dockerfile @@ -40,9 +40,9 @@ WORKDIR / COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh -RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-25/wasi-sdk-25.0-x86_64-linux.tar.gz | \ +RUN curl -L https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-27/wasi-sdk-27.0-x86_64-linux.tar.gz | \ tar -xz -ENV WASI_SDK_PATH=/wasi-sdk-25.0-x86_64-linux +ENV WASI_SDK_PATH=/wasi-sdk-27.0-x86_64-linux ENV RUST_CONFIGURE_ARGS \ --musl-root-x86_64=/usr/local/x86_64-linux-musl \ From 307fd41123bd9ce4ff9e4e9767f3f7771d91d789 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Wed, 16 Jul 2025 19:42:23 +0200 Subject: [PATCH 185/201] tests: Test line number in debuginfo for diverging function calls --- .../diverging-function-call-debuginfo.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/codegen-llvm/diverging-function-call-debuginfo.rs diff --git a/tests/codegen-llvm/diverging-function-call-debuginfo.rs b/tests/codegen-llvm/diverging-function-call-debuginfo.rs new file mode 100644 index 000000000000..1a80fe1643de --- /dev/null +++ b/tests/codegen-llvm/diverging-function-call-debuginfo.rs @@ -0,0 +1,38 @@ +/// Make sure that line debuginfo is correct for diverging calls under certain +/// conditions. In particular we want to ensure that the line number is never +/// 0, but we check the absence of 0 by looking for the expected exact line +/// numbers. Regression test for . + +//@ compile-flags: -g -Clto -Copt-level=0 +//@ no-prefer-dynamic + +// First find the scope of both diverge() calls, namely this main() function. +// CHECK-DAG: [[MAIN_SCOPE:![0-9]+]] = distinct !DISubprogram(name: "main", linkageName: {{.*}}diverging_function_call_debuginfo{{.*}}main{{.*}} +fn main() { + if True == False { + // unreachable + // Then find the DILocation with the correct line number for this call ... + // CHECK-DAG: [[UNREACHABLE_CALL_DBG:![0-9]+]] = !DILocation(line: [[@LINE+1]], {{.*}}scope: [[MAIN_SCOPE]] + diverge(); + } + + // ... and this call. + // CHECK-DAG: [[LAST_CALL_DBG:![0-9]+]] = !DILocation(line: [[@LINE+1]], {{.*}}scope: [[MAIN_SCOPE]] + diverge(); +} + +#[derive(PartialEq)] +pub enum MyBool { + True, + False, +} + +use MyBool::*; + +fn diverge() -> ! { + panic!(); +} + +// Finally make sure both DILocations belong to each the respective diverge() call. +// CHECK-DAG: call void {{.*}}diverging_function_call_debuginfo{{.*}}diverge{{.*}} !dbg [[LAST_CALL_DBG]] +// CHECK-DAG: call void {{.*}}diverging_function_call_debuginfo{{.*}}diverge{{.*}} !dbg [[UNREACHABLE_CALL_DBG]] From cffde732ceecd1bcb333d34fe7a9c9caa4d9a9fa Mon Sep 17 00:00:00 2001 From: Daniel Paoliello Date: Wed, 16 Jul 2025 16:16:24 -0700 Subject: [PATCH 186/201] Verify llvm-needs-components are not empty and match the --target value --- .../rustc-dev-guide/src/tests/directives.md | 13 +- src/tools/compiletest/src/directives.rs | 184 ++++++++++++++---- .../compiletest/src/directives/auxiliary.rs | 41 +++- src/tools/compiletest/src/runtest/debugger.rs | 8 +- src/tools/tidy/src/style.rs | 7 +- src/tools/tidy/src/target_specific_tests.rs | 61 +++++- tests/codegen-llvm/abi-efiapi.rs | 10 +- tests/codegen-llvm/cast-target-abi.rs | 2 +- tests/codegen-llvm/cf-protection.rs | 2 +- tests/codegen-llvm/codemodels.rs | 2 +- tests/codegen-llvm/ehcontguard_disabled.rs | 2 - .../codegen-llvm/naked-fn/naked-functions.rs | 2 +- .../address-sanitizer-globals-tracking.rs | 6 +- ...it-kcfi-operand-bundle-attr-no-sanitize.rs | 2 +- ...rand-bundle-itanium-cxx-abi-generalized.rs | 2 +- ...-itanium-cxx-abi-normalized-generalized.rs | 2 +- ...erand-bundle-itanium-cxx-abi-normalized.rs | 2 +- ...mit-kcfi-operand-bundle-itanium-cxx-abi.rs | 2 +- .../kcfi/emit-kcfi-operand-bundle.rs | 2 +- .../kcfi/emit-type-metadata-trait-objects.rs | 2 +- .../sanitizer/memory-track-origins.rs | 2 +- tests/codegen-llvm/ub-checks.rs | 2 +- tests/debuginfo/unsized.rs | 1 - tests/ui/errors/remap-path-prefix-sysroot.rs | 2 +- tests/ui/errors/wrong-target-spec.rs | 1 + tests/ui/linkage-attr/unstable-flavor.rs | 4 +- tests/ui/target_modifiers/defaults_check.rs | 2 +- .../target_modifiers/incompatible_fixedx18.rs | 2 +- .../target_modifiers/incompatible_regparm.rs | 2 +- tests/ui/target_modifiers/no_value_bool.rs | 2 +- 30 files changed, 277 insertions(+), 97 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 5c3ae359ba0b..89e4d3e9b58e 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -293,8 +293,6 @@ See [Pretty-printer](compiletest.md#pretty-printer-tests). - `no-auto-check-cfg` — disable auto check-cfg (only for `--check-cfg` tests) - [`revisions`](compiletest.md#revisions) — compile multiple times -- [`unused-revision-names`](compiletest.md#ignoring-unused-revision-names) - - suppress tidy checks for mentioning unknown revision names -[`forbid-output`](compiletest.md#incremental-tests) — incremental cfail rejects output pattern - [`should-ice`](compiletest.md#incremental-tests) — incremental cfail should @@ -315,6 +313,17 @@ test suites that use those tools: - `llvm-cov-flags` adds extra flags when running LLVM's `llvm-cov` tool. - Used by [coverage tests](compiletest.md#coverage-tests) in `coverage-run` mode. +### Tidy specific directives + +The following directives control how the [tidy script](../conventions.md#formatting) +verifies tests. + +- `ignore-tidy-target-specific-tests` disables checking that the appropriate + LLVM component is required (via a `needs-llvm-components` directive) when a + test is compiled for a specific target (via the `--target` flag in a + `compile-flag` directive). +- [`unused-revision-names`](compiletest.md#ignoring-unused-revision-names) - + suppress tidy checks for mentioning unknown revision names. ## Substitutions diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index a1f76a075562..69ff846330ed 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -59,9 +59,9 @@ impl EarlyProps { &mut poisoned, testfile, rdr, - &mut |DirectiveLine { raw_directive: ln, .. }| { - parse_and_update_aux(config, ln, &mut props.aux); - config.parse_and_update_revisions(testfile, ln, &mut props.revisions); + &mut |DirectiveLine { line_number, raw_directive: ln, .. }| { + parse_and_update_aux(config, ln, testfile, line_number, &mut props.aux); + config.parse_and_update_revisions(testfile, line_number, ln, &mut props.revisions); }, ); @@ -351,7 +351,7 @@ impl TestProps { &mut poisoned, testfile, file, - &mut |directive @ DirectiveLine { raw_directive: ln, .. }| { + &mut |directive @ DirectiveLine { line_number, raw_directive: ln, .. }| { if !directive.applies_to_test_revision(test_revision) { return; } @@ -361,17 +361,28 @@ impl TestProps { config.push_name_value_directive( ln, ERROR_PATTERN, + testfile, + line_number, &mut self.error_patterns, |r| r, ); config.push_name_value_directive( ln, REGEX_ERROR_PATTERN, + testfile, + line_number, &mut self.regex_error_patterns, |r| r, ); - config.push_name_value_directive(ln, DOC_FLAGS, &mut self.doc_flags, |r| r); + config.push_name_value_directive( + ln, + DOC_FLAGS, + testfile, + line_number, + &mut self.doc_flags, + |r| r, + ); fn split_flags(flags: &str) -> Vec { // Individual flags can be single-quoted to preserve spaces; see @@ -386,7 +397,9 @@ impl TestProps { .collect::>() } - if let Some(flags) = config.parse_name_value_directive(ln, COMPILE_FLAGS) { + if let Some(flags) = + config.parse_name_value_directive(ln, COMPILE_FLAGS, testfile, line_number) + { let flags = split_flags(&flags); for flag in &flags { if flag == "--edition" || flag.starts_with("--edition=") { @@ -395,25 +408,40 @@ impl TestProps { } self.compile_flags.extend(flags); } - if config.parse_name_value_directive(ln, INCORRECT_COMPILER_FLAGS).is_some() { + if config + .parse_name_value_directive( + ln, + INCORRECT_COMPILER_FLAGS, + testfile, + line_number, + ) + .is_some() + { panic!("`compiler-flags` directive should be spelled `compile-flags`"); } - if let Some(edition) = config.parse_edition(ln) { + if let Some(edition) = config.parse_edition(ln, testfile, line_number) { // The edition is added at the start, since flags from //@compile-flags must // be passed to rustc last. self.compile_flags.insert(0, format!("--edition={}", edition.trim())); has_edition = true; } - config.parse_and_update_revisions(testfile, ln, &mut self.revisions); + config.parse_and_update_revisions( + testfile, + line_number, + ln, + &mut self.revisions, + ); - if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) { + if let Some(flags) = + config.parse_name_value_directive(ln, RUN_FLAGS, testfile, line_number) + { self.run_flags.extend(split_flags(&flags)); } if self.pp_exact.is_none() { - self.pp_exact = config.parse_pp_exact(ln, testfile); + self.pp_exact = config.parse_pp_exact(ln, testfile, line_number); } config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice); @@ -435,7 +463,9 @@ impl TestProps { ); config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic); - if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) { + if let Some(m) = + config.parse_name_value_directive(ln, PRETTY_MODE, testfile, line_number) + { self.pretty_mode = m; } @@ -446,35 +476,45 @@ impl TestProps { ); // Call a helper method to deal with aux-related directives. - parse_and_update_aux(config, ln, &mut self.aux); + parse_and_update_aux(config, ln, testfile, line_number, &mut self.aux); config.push_name_value_directive( ln, EXEC_ENV, + testfile, + line_number, &mut self.exec_env, Config::parse_env, ); config.push_name_value_directive( ln, UNSET_EXEC_ENV, + testfile, + line_number, &mut self.unset_exec_env, |r| r.trim().to_owned(), ); config.push_name_value_directive( ln, RUSTC_ENV, + testfile, + line_number, &mut self.rustc_env, Config::parse_env, ); config.push_name_value_directive( ln, UNSET_RUSTC_ENV, + testfile, + line_number, &mut self.unset_rustc_env, |r| r.trim().to_owned(), ); config.push_name_value_directive( ln, FORBID_OUTPUT, + testfile, + line_number, &mut self.forbid_output, |r| r, ); @@ -510,7 +550,7 @@ impl TestProps { } if let Some(code) = config - .parse_name_value_directive(ln, FAILURE_STATUS) + .parse_name_value_directive(ln, FAILURE_STATUS, testfile, line_number) .and_then(|code| code.trim().parse::().ok()) { self.failure_status = Some(code); @@ -531,6 +571,8 @@ impl TestProps { config.set_name_value_directive( ln, ASSEMBLY_OUTPUT, + testfile, + line_number, &mut self.assembly_output, |r| r.trim().to_string(), ); @@ -543,7 +585,9 @@ impl TestProps { // Unlike the other `name_value_directive`s this needs to be handled manually, // because it sets a `bool` flag. - if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) { + if let Some(known_bug) = + config.parse_name_value_directive(ln, KNOWN_BUG, testfile, line_number) + { let known_bug = known_bug.trim(); if known_bug == "unknown" || known_bug.split(',').all(|issue_ref| { @@ -571,16 +615,25 @@ impl TestProps { config.set_name_value_directive( ln, TEST_MIR_PASS, + testfile, + line_number, &mut self.mir_unit_test, |s| s.trim().to_string(), ); config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base); - if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) { + if let Some(flags) = + config.parse_name_value_directive(ln, LLVM_COV_FLAGS, testfile, line_number) + { self.llvm_cov_flags.extend(split_flags(&flags)); } - if let Some(flags) = config.parse_name_value_directive(ln, FILECHECK_FLAGS) { + if let Some(flags) = config.parse_name_value_directive( + ln, + FILECHECK_FLAGS, + testfile, + line_number, + ) { self.filecheck_flags.extend(split_flags(&flags)); } @@ -588,9 +641,12 @@ impl TestProps { self.update_add_core_stubs(ln, config); - if let Some(err_kind) = - config.parse_name_value_directive(ln, DONT_REQUIRE_ANNOTATIONS) - { + if let Some(err_kind) = config.parse_name_value_directive( + ln, + DONT_REQUIRE_ANNOTATIONS, + testfile, + line_number, + ) { self.dont_require_annotations .insert(ErrorKind::expect_from_user_str(err_kind.trim())); } @@ -1206,6 +1262,7 @@ impl Config { fn parse_and_update_revisions( &self, testfile: &Utf8Path, + line_number: usize, line: &str, existing: &mut Vec, ) { @@ -1219,7 +1276,8 @@ impl Config { const FILECHECK_FORBIDDEN_REVISION_NAMES: [&str; 9] = ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"]; - if let Some(raw) = self.parse_name_value_directive(line, "revisions") { + if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number) + { if self.mode == TestMode::RunMake { panic!("`run-make` tests do not support revisions: {}", testfile); } @@ -1264,8 +1322,13 @@ impl Config { (name.to_owned(), value.to_owned()) } - fn parse_pp_exact(&self, line: &str, testfile: &Utf8Path) -> Option { - if let Some(s) = self.parse_name_value_directive(line, "pp-exact") { + fn parse_pp_exact( + &self, + line: &str, + testfile: &Utf8Path, + line_number: usize, + ) -> Option { + if let Some(s) = self.parse_name_value_directive(line, "pp-exact", testfile, line_number) { Some(Utf8PathBuf::from(&s)) } else if self.parse_name_directive(line, "pp-exact") { testfile.file_name().map(Utf8PathBuf::from) @@ -1306,19 +1369,31 @@ impl Config { line.starts_with("no-") && self.parse_name_directive(&line[3..], directive) } - pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option { + pub fn parse_name_value_directive( + &self, + line: &str, + directive: &str, + testfile: &Utf8Path, + line_number: usize, + ) -> Option { let colon = directive.len(); if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') { let value = line[(colon + 1)..].to_owned(); debug!("{}: {}", directive, value); - Some(expand_variables(value, self)) + let value = expand_variables(value, self); + if value.is_empty() { + error!("{testfile}:{line_number}: empty value for directive `{directive}`"); + help!("expected syntax is: `{directive}: value`"); + panic!("empty directive value detected"); + } + Some(value) } else { None } } - fn parse_edition(&self, line: &str) -> Option { - self.parse_name_value_directive(line, "edition") + fn parse_edition(&self, line: &str, testfile: &Utf8Path, line_number: usize) -> Option { + self.parse_name_value_directive(line, "edition", testfile, line_number) } fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) { @@ -1340,11 +1415,14 @@ impl Config { &self, line: &str, directive: &str, + testfile: &Utf8Path, + line_number: usize, value: &mut Option, parse: impl FnOnce(String) -> T, ) { if value.is_none() { - *value = self.parse_name_value_directive(line, directive).map(parse); + *value = + self.parse_name_value_directive(line, directive, testfile, line_number).map(parse); } } @@ -1352,10 +1430,14 @@ impl Config { &self, line: &str, directive: &str, + testfile: &Utf8Path, + line_number: usize, values: &mut Vec, parse: impl FnOnce(String) -> T, ) { - if let Some(value) = self.parse_name_value_directive(line, directive).map(parse) { + if let Some(value) = + self.parse_name_value_directive(line, directive, testfile, line_number).map(parse) + { values.push(value); } } @@ -1672,9 +1754,9 @@ pub(crate) fn make_test_description( decision!(cfg::handle_ignore(config, ln)); decision!(cfg::handle_only(config, ln)); decision!(needs::handle_needs(&cache.needs, config, ln)); - decision!(ignore_llvm(config, path, ln)); - decision!(ignore_backends(config, path, ln)); - decision!(needs_backends(config, path, ln)); + decision!(ignore_llvm(config, path, ln, line_number)); + decision!(ignore_backends(config, path, ln, line_number)); + decision!(needs_backends(config, path, ln, line_number)); decision!(ignore_cdb(config, ln)); decision!(ignore_gdb(config, ln)); decision!(ignore_lldb(config, ln)); @@ -1801,8 +1883,15 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision { IgnoreDecision::Continue } -fn ignore_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { - if let Some(backends_to_ignore) = config.parse_name_value_directive(line, "ignore-backends") { +fn ignore_backends( + config: &Config, + path: &Utf8Path, + line: &str, + line_number: usize, +) -> IgnoreDecision { + if let Some(backends_to_ignore) = + config.parse_name_value_directive(line, "ignore-backends", path, line_number) + { for backend in backends_to_ignore.split_whitespace().map(|backend| { match CodegenBackend::try_from(backend) { Ok(backend) => backend, @@ -1821,8 +1910,15 @@ fn ignore_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecisi IgnoreDecision::Continue } -fn needs_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { - if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends") { +fn needs_backends( + config: &Config, + path: &Utf8Path, + line: &str, + line_number: usize, +) -> IgnoreDecision { + if let Some(needed_backends) = + config.parse_name_value_directive(line, "needs-backends", path, line_number) + { if !needed_backends .split_whitespace() .map(|backend| match CodegenBackend::try_from(backend) { @@ -1844,9 +1940,9 @@ fn needs_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecisio IgnoreDecision::Continue } -fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { +fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str, line_number: usize) -> IgnoreDecision { if let Some(needed_components) = - config.parse_name_value_directive(line, "needs-llvm-components") + config.parse_name_value_directive(line, "needs-llvm-components", path, line_number) { let components: HashSet<_> = config.llvm_components.split_whitespace().collect(); if let Some(missing_component) = needed_components @@ -1867,7 +1963,9 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { if let Some(actual_version) = &config.llvm_version { // Note that these `min` versions will check for not just major versions. - if let Some(version_string) = config.parse_name_value_directive(line, "min-llvm-version") { + if let Some(version_string) = + config.parse_name_value_directive(line, "min-llvm-version", path, line_number) + { let min_version = extract_llvm_version(&version_string); // Ignore if actual version is smaller than the minimum required version. if *actual_version < min_version { @@ -1878,7 +1976,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { }; } } else if let Some(version_string) = - config.parse_name_value_directive(line, "max-llvm-major-version") + config.parse_name_value_directive(line, "max-llvm-major-version", path, line_number) { let max_version = extract_llvm_version(&version_string); // Ignore if actual major version is larger than the maximum required major version. @@ -1892,7 +1990,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { }; } } else if let Some(version_string) = - config.parse_name_value_directive(line, "min-system-llvm-version") + config.parse_name_value_directive(line, "min-system-llvm-version", path, line_number) { let min_version = extract_llvm_version(&version_string); // Ignore if using system LLVM and actual version @@ -1905,7 +2003,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { }; } } else if let Some(version_range) = - config.parse_name_value_directive(line, "ignore-llvm-version") + config.parse_name_value_directive(line, "ignore-llvm-version", path, line_number) { // Syntax is: "ignore-llvm-version: [- ]" let (v_min, v_max) = @@ -1931,7 +2029,7 @@ fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision { } } } else if let Some(version_string) = - config.parse_name_value_directive(line, "exact-llvm-major-version") + config.parse_name_value_directive(line, "exact-llvm-major-version", path, line_number) { // Syntax is "exact-llvm-major-version: " let version = extract_llvm_version(&version_string); diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs index cdb75f6ffa90..7c1ed2e70062 100644 --- a/src/tools/compiletest/src/directives/auxiliary.rs +++ b/src/tools/compiletest/src/directives/auxiliary.rs @@ -3,6 +3,8 @@ use std::iter; +use camino::Utf8Path; + use super::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO}; use crate::common::Config; @@ -41,17 +43,42 @@ impl AuxProps { /// If the given test directive line contains an `aux-*` directive, parse it /// and update [`AuxProps`] accordingly. -pub(super) fn parse_and_update_aux(config: &Config, ln: &str, aux: &mut AuxProps) { +pub(super) fn parse_and_update_aux( + config: &Config, + ln: &str, + testfile: &Utf8Path, + line_number: usize, + aux: &mut AuxProps, +) { if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) { return; } - config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string()); - config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string()); - config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate); - config - .push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, |r| r.trim().to_string()); - if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) { + config.push_name_value_directive(ln, AUX_BUILD, testfile, line_number, &mut aux.builds, |r| { + r.trim().to_string() + }); + config.push_name_value_directive(ln, AUX_BIN, testfile, line_number, &mut aux.bins, |r| { + r.trim().to_string() + }); + config.push_name_value_directive( + ln, + AUX_CRATE, + testfile, + line_number, + &mut aux.crates, + parse_aux_crate, + ); + config.push_name_value_directive( + ln, + PROC_MACRO, + testfile, + line_number, + &mut aux.proc_macros, + |r| r.trim().to_string(), + ); + if let Some(r) = + config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND, testfile, line_number) + { aux.codegen_backend = Some(r.trim().to_owned()); } } diff --git a/src/tools/compiletest/src/runtest/debugger.rs b/src/tools/compiletest/src/runtest/debugger.rs index a4103c5b4a9a..ba824124e875 100644 --- a/src/tools/compiletest/src/runtest/debugger.rs +++ b/src/tools/compiletest/src/runtest/debugger.rs @@ -47,10 +47,14 @@ impl DebuggerCommands { continue; }; - if let Some(command) = config.parse_name_value_directive(&line, &command_directive) { + if let Some(command) = + config.parse_name_value_directive(&line, &command_directive, file, line_no) + { commands.push(command); } - if let Some(pattern) = config.parse_name_value_directive(&line, &check_directive) { + if let Some(pattern) = + config.parse_name_value_directive(&line, &check_directive, file, line_no) + { check_lines.push((line_no, pattern)); } } diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs index 35ed61eacc73..fca097c091b7 100644 --- a/src/tools/tidy/src/style.rs +++ b/src/tools/tidy/src/style.rs @@ -519,8 +519,11 @@ pub fn check(path: &Path, bad: &mut bool) { .any(|directive| matches!(directive, Directive::Ignore(_))); let has_alphabetical_directive = line.contains("tidy-alphabetical-start") || line.contains("tidy-alphabetical-end"); - let has_recognized_directive = - has_recognized_ignore_directive || has_alphabetical_directive; + let has_other_tidy_ignore_directive = + line.contains("ignore-tidy-target-specific-tests"); + let has_recognized_directive = has_recognized_ignore_directive + || has_alphabetical_directive + || has_other_tidy_ignore_directive; if contains_potential_directive && (!has_recognized_directive) { err("Unrecognized tidy directive") } diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs index f4a6783abb67..b2d5f259eb2d 100644 --- a/src/tools/tidy/src/target_specific_tests.rs +++ b/src/tools/tidy/src/target_specific_tests.rs @@ -12,12 +12,16 @@ const COMPILE_FLAGS_HEADER: &str = "compile-flags:"; #[derive(Default, Debug)] struct RevisionInfo<'a> { - target_arch: Option<&'a str>, + target_arch: Option>, llvm_components: Option>, } pub fn check(tests_path: &Path, bad: &mut bool) { crate::walk::walk(tests_path, |path, _is_dir| filter_not_rust(path), &mut |entry, content| { + if content.contains("// ignore-tidy-target-specific-tests") { + return; + } + let file = entry.path().display(); let mut header_map = BTreeMap::new(); iter_header(content, &mut |HeaderLine { revision, directive, .. }| { @@ -34,10 +38,11 @@ pub fn check(tests_path: &Path, bad: &mut bool) { && let Some((_, v)) = compile_flags.split_once("--target") { let v = v.trim_start_matches([' ', '=']); - let v = if v == "{{target}}" { Some((v, v)) } else { v.split_once("-") }; - if let Some((arch, _)) = v { - let info = header_map.entry(revision).or_insert(RevisionInfo::default()); - info.target_arch.replace(arch); + let info = header_map.entry(revision).or_insert(RevisionInfo::default()); + if v.starts_with("{{") { + info.target_arch.replace(None); + } else if let Some((arch, _)) = v.split_once("-") { + info.target_arch.replace(Some(arch)); } else { eprintln!("{file}: seems to have a malformed --target value"); *bad = true; @@ -54,9 +59,11 @@ pub fn check(tests_path: &Path, bad: &mut bool) { let rev = rev.unwrap_or("[unspecified]"); match (target_arch, llvm_components) { (None, None) => {} - (Some(_), None) => { + (Some(target_arch), None) => { + let llvm_component = + target_arch.map_or_else(|| "".to_string(), arch_to_llvm_component); eprintln!( - "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER}` as it has `--target` set" + "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" ); *bad = true; } @@ -66,11 +73,45 @@ pub fn check(tests_path: &Path, bad: &mut bool) { ); *bad = true; } - (Some(_), Some(_)) => { - // FIXME: check specified components against the target architectures we - // gathered. + (Some(target_arch), Some(llvm_components)) => { + if let Some(target_arch) = target_arch { + let llvm_component = arch_to_llvm_component(target_arch); + if !llvm_components.contains(&llvm_component.as_str()) { + eprintln!( + "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set" + ); + *bad = true; + } + } } } } }); } + +fn arch_to_llvm_component(arch: &str) -> String { + // NOTE: This is an *approximate* mapping of Rust's `--target` architecture to LLVM component + // names. It is not intended to be an authoritative source, but rather a best-effort that's good + // enough for the purpose of this tidy check. + match arch { + "amdgcn" => "amdgpu".into(), + "aarch64_be" | "arm64_32" | "arm64e" | "arm64ec" => "aarch64".into(), + "i386" | "i586" | "i686" | "x86" | "x86_64" | "x86_64h" => "x86".into(), + "loongarch32" | "loongarch64" => "loongarch".into(), + "nvptx64" => "nvptx".into(), + "s390x" => "systemz".into(), + "sparc64" | "sparcv9" => "sparc".into(), + "wasm32" | "wasm32v1" | "wasm64" => "webassembly".into(), + _ if arch.starts_with("armeb") + || arch.starts_with("armv") + || arch.starts_with("thumbv") => + { + "arm".into() + } + _ if arch.starts_with("bpfe") => "bpf".into(), + _ if arch.starts_with("mips") => "mips".into(), + _ if arch.starts_with("powerpc") => "powerpc".into(), + _ if arch.starts_with("riscv") => "riscv".into(), + _ => arch.to_ascii_lowercase(), + } +} diff --git a/tests/codegen-llvm/abi-efiapi.rs b/tests/codegen-llvm/abi-efiapi.rs index 1736f0daf0fa..4cd645101a8b 100644 --- a/tests/codegen-llvm/abi-efiapi.rs +++ b/tests/codegen-llvm/abi-efiapi.rs @@ -3,15 +3,15 @@ //@ add-core-stubs //@ revisions:x86_64 i686 aarch64 arm riscv //@[x86_64] compile-flags: --target x86_64-unknown-uefi -//@[x86_64] needs-llvm-components: aarch64 arm riscv +//@[x86_64] needs-llvm-components: x86 //@[i686] compile-flags: --target i686-unknown-linux-musl -//@[i686] needs-llvm-components: aarch64 arm riscv +//@[i686] needs-llvm-components: x86 //@[aarch64] compile-flags: --target aarch64-unknown-none -//@[aarch64] needs-llvm-components: aarch64 arm riscv +//@[aarch64] needs-llvm-components: aarch64 //@[arm] compile-flags: --target armv7r-none-eabi -//@[arm] needs-llvm-components: aarch64 arm riscv +//@[arm] needs-llvm-components: arm //@[riscv] compile-flags: --target riscv64gc-unknown-none-elf -//@[riscv] needs-llvm-components: aarch64 arm riscv +//@[riscv] needs-llvm-components: riscv //@ compile-flags: -C no-prepopulate-passes #![crate_type = "lib"] diff --git a/tests/codegen-llvm/cast-target-abi.rs b/tests/codegen-llvm/cast-target-abi.rs index cbd49e2f022a..28d3ad5f61c7 100644 --- a/tests/codegen-llvm/cast-target-abi.rs +++ b/tests/codegen-llvm/cast-target-abi.rs @@ -4,7 +4,7 @@ //@ compile-flags: -Copt-level=3 -Cno-prepopulate-passes -Zlint-llvm-ir //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] needs-llvm-components: arm +//@[aarch64] needs-llvm-components: aarch64 //@[loongarch64] compile-flags: --target loongarch64-unknown-linux-gnu //@[loongarch64] needs-llvm-components: loongarch //@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu diff --git a/tests/codegen-llvm/cf-protection.rs b/tests/codegen-llvm/cf-protection.rs index f1349a5dcb98..9efadb599322 100644 --- a/tests/codegen-llvm/cf-protection.rs +++ b/tests/codegen-llvm/cf-protection.rs @@ -3,7 +3,7 @@ //@ add-core-stubs //@ revisions: undefined none branch return full //@ needs-llvm-components: x86 -//@ [undefined] compile-flags: +// [undefined] no extra compile-flags //@ [none] compile-flags: -Z cf-protection=none //@ [branch] compile-flags: -Z cf-protection=branch //@ [return] compile-flags: -Z cf-protection=return diff --git a/tests/codegen-llvm/codemodels.rs b/tests/codegen-llvm/codemodels.rs index 06d2eade78ae..e82e094aab8a 100644 --- a/tests/codegen-llvm/codemodels.rs +++ b/tests/codegen-llvm/codemodels.rs @@ -1,7 +1,7 @@ //@ only-x86_64 //@ revisions: NOMODEL MODEL-SMALL MODEL-KERNEL MODEL-MEDIUM MODEL-LARGE -//@[NOMODEL] compile-flags: +// [NOMODEL] no compile-flags //@[MODEL-SMALL] compile-flags: -C code-model=small //@[MODEL-KERNEL] compile-flags: -C code-model=kernel //@[MODEL-MEDIUM] compile-flags: -C code-model=medium diff --git a/tests/codegen-llvm/ehcontguard_disabled.rs b/tests/codegen-llvm/ehcontguard_disabled.rs index 9efb2721b3e8..962d14e7eb9b 100644 --- a/tests/codegen-llvm/ehcontguard_disabled.rs +++ b/tests/codegen-llvm/ehcontguard_disabled.rs @@ -1,5 +1,3 @@ -//@ compile-flags: - #![crate_type = "lib"] // A basic test function. diff --git a/tests/codegen-llvm/naked-fn/naked-functions.rs b/tests/codegen-llvm/naked-fn/naked-functions.rs index 344af6eb42fa..8a7ee4b4de56 100644 --- a/tests/codegen-llvm/naked-fn/naked-functions.rs +++ b/tests/codegen-llvm/naked-fn/naked-functions.rs @@ -8,7 +8,7 @@ //@[win_i686] compile-flags: --target i686-pc-windows-gnu //@[win_i686] needs-llvm-components: x86 //@[macos] compile-flags: --target aarch64-apple-darwin -//@[macos] needs-llvm-components: arm +//@[macos] needs-llvm-components: aarch64 //@[thumb] compile-flags: --target thumbv7em-none-eabi //@[thumb] needs-llvm-components: arm diff --git a/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs b/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs index f319306f93fd..642bf5e75762 100644 --- a/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs +++ b/tests/codegen-llvm/sanitizer/address-sanitizer-globals-tracking.rs @@ -19,9 +19,9 @@ //@ only-linux // //@ revisions:ASAN ASAN-FAT-LTO -//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static -//@[ASAN] compile-flags: -//@[ASAN-FAT-LTO] compile-flags: -Cprefer-dynamic=false -Clto=fat +//@ compile-flags: -Zsanitizer=address -Ctarget-feature=-crt-static +// [ASAN] no extra compile-flags +//@[ASAN-FAT-LTO] compile-flags: -Cprefer-dynamic=false -Clto=fat #![crate_type = "staticlib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs index 6b40918dd3af..02c31fb8e9b0 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-attr-no-sanitize.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs index 942b50deb028..9a60d51713f5 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-generalized.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-generalize-pointers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs index c89d9bdd1217..134f4ff4bfd9 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized-generalized.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers -Zsanitizer-cfi-generalize-pointers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs index 220cae1a4fa3..4328b7fa07df 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi-normalized.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Zsanitizer-cfi-normalize-integers #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs index bb9a00059030..81a9db1b97a5 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle-itanium-cxx-abi.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs index 8b844b991429..61056c2a54e7 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-kcfi-operand-bundle.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs index 15c107bea158..182af162d782 100644 --- a/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs +++ b/tests/codegen-llvm/sanitizer/kcfi/emit-type-metadata-trait-objects.rs @@ -5,7 +5,7 @@ //@ [aarch64] compile-flags: --target aarch64-unknown-none //@ [aarch64] needs-llvm-components: aarch64 //@ [x86_64] compile-flags: --target x86_64-unknown-none -//@ [x86_64] needs-llvm-components: +//@ [x86_64] needs-llvm-components: x86 //@ compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0 #![crate_type = "lib"] diff --git a/tests/codegen-llvm/sanitizer/memory-track-origins.rs b/tests/codegen-llvm/sanitizer/memory-track-origins.rs index 318c277e10cb..5eb5b356b051 100644 --- a/tests/codegen-llvm/sanitizer/memory-track-origins.rs +++ b/tests/codegen-llvm/sanitizer/memory-track-origins.rs @@ -5,7 +5,7 @@ //@ revisions:MSAN-0 MSAN-1 MSAN-2 MSAN-1-LTO MSAN-2-LTO // //@ compile-flags: -Zsanitizer=memory -Ctarget-feature=-crt-static -//@[MSAN-0] compile-flags: +// [MSAN-0] no extra compile-flags //@[MSAN-1] compile-flags: -Zsanitizer-memory-track-origins=1 //@[MSAN-2] compile-flags: -Zsanitizer-memory-track-origins //@[MSAN-1-LTO] compile-flags: -Zsanitizer-memory-track-origins=1 -C lto=fat diff --git a/tests/codegen-llvm/ub-checks.rs b/tests/codegen-llvm/ub-checks.rs index 67f5bff08d5b..c40bc9acc52a 100644 --- a/tests/codegen-llvm/ub-checks.rs +++ b/tests/codegen-llvm/ub-checks.rs @@ -6,7 +6,7 @@ // but ub-checks are explicitly disabled. //@ revisions: DEBUG NOCHECKS -//@ [DEBUG] compile-flags: +// [DEBUG] no extra compile-flags //@ [NOCHECKS] compile-flags: -Zub-checks=no //@ compile-flags: -Copt-level=3 -Cdebug-assertions=yes diff --git a/tests/debuginfo/unsized.rs b/tests/debuginfo/unsized.rs index 17c5e463fbac..edd9f5af557d 100644 --- a/tests/debuginfo/unsized.rs +++ b/tests/debuginfo/unsized.rs @@ -37,7 +37,6 @@ // cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] // cdb-command:dx _box -// cdb-check: // cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] // cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] // cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[4]] diff --git a/tests/ui/errors/remap-path-prefix-sysroot.rs b/tests/ui/errors/remap-path-prefix-sysroot.rs index 5e2e4fab51de..f4a2766ff4c0 100644 --- a/tests/ui/errors/remap-path-prefix-sysroot.rs +++ b/tests/ui/errors/remap-path-prefix-sysroot.rs @@ -2,7 +2,7 @@ //@ compile-flags: -g -Ztranslate-remapped-path-to-local-path=yes //@ [with-remap]compile-flags: --remap-path-prefix={{rust-src-base}}=remapped //@ [with-remap]compile-flags: --remap-path-prefix={{src-base}}=remapped-tests-ui -//@ [without-remap]compile-flags: +// [without-remap] no extra compile-flags // The $SRC_DIR*.rs:LL:COL normalisation doesn't kick in automatically // as the remapped revision will not begin with $SRC_DIR_REAL, diff --git a/tests/ui/errors/wrong-target-spec.rs b/tests/ui/errors/wrong-target-spec.rs index a3a0e05d8266..1a9768881122 100644 --- a/tests/ui/errors/wrong-target-spec.rs +++ b/tests/ui/errors/wrong-target-spec.rs @@ -2,6 +2,7 @@ // checks that such invalid target specs are rejected by the compiler. // See https://github.com/rust-lang/rust/issues/33329 +// ignore-tidy-target-specific-tests //@ needs-llvm-components: x86 //@ compile-flags: --target x86_64_unknown-linux-musl diff --git a/tests/ui/linkage-attr/unstable-flavor.rs b/tests/ui/linkage-attr/unstable-flavor.rs index 6aa9efb58d1f..5412e248f341 100644 --- a/tests/ui/linkage-attr/unstable-flavor.rs +++ b/tests/ui/linkage-attr/unstable-flavor.rs @@ -4,9 +4,9 @@ // //@ revisions: bpf ptx //@ [bpf] compile-flags: --target=bpfel-unknown-none -C linker-flavor=bpf --crate-type=rlib -//@ [bpf] needs-llvm-components: +//@ [bpf] needs-llvm-components: bpf //@ [ptx] compile-flags: --target=nvptx64-nvidia-cuda -C linker-flavor=ptx --crate-type=rlib -//@ [ptx] needs-llvm-components: +//@ [ptx] needs-llvm-components: nvptx #![feature(no_core)] #![no_core] diff --git a/tests/ui/target_modifiers/defaults_check.rs b/tests/ui/target_modifiers/defaults_check.rs index de72acd32bc1..ce1d534fd757 100644 --- a/tests/ui/target_modifiers/defaults_check.rs +++ b/tests/ui/target_modifiers/defaults_check.rs @@ -6,7 +6,7 @@ //@ needs-llvm-components: x86 //@ revisions: ok ok_explicit error -//@[ok] compile-flags: +// [ok] no extra compile-flags //@[ok_explicit] compile-flags: -Zreg-struct-return=false //@[error] compile-flags: -Zreg-struct-return=true //@[ok] check-pass diff --git a/tests/ui/target_modifiers/incompatible_fixedx18.rs b/tests/ui/target_modifiers/incompatible_fixedx18.rs index 6c13984f608e..5ba676406ee3 100644 --- a/tests/ui/target_modifiers/incompatible_fixedx18.rs +++ b/tests/ui/target_modifiers/incompatible_fixedx18.rs @@ -5,7 +5,7 @@ //@ revisions:allow_match allow_mismatch error_generated //@[allow_match] compile-flags: -Zfixed-x18 //@[allow_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=fixed-x18 -//@[error_generated] compile-flags: +// [error_generated] no extra compile-flags //@[allow_mismatch] check-pass //@[allow_match] check-pass diff --git a/tests/ui/target_modifiers/incompatible_regparm.rs b/tests/ui/target_modifiers/incompatible_regparm.rs index 395c26fc2c02..e76bfc976a19 100644 --- a/tests/ui/target_modifiers/incompatible_regparm.rs +++ b/tests/ui/target_modifiers/incompatible_regparm.rs @@ -5,7 +5,7 @@ //@ revisions:allow_regparm_mismatch allow_no_value error_generated //@[allow_regparm_mismatch] compile-flags: -Cunsafe-allow-abi-mismatch=regparm //@[allow_no_value] compile-flags: -Cunsafe-allow-abi-mismatch -//@[error_generated] compile-flags: +// [error_generated] no extra compile-flags //@[allow_regparm_mismatch] check-pass #![feature(no_core)] diff --git a/tests/ui/target_modifiers/no_value_bool.rs b/tests/ui/target_modifiers/no_value_bool.rs index ceba40afa896..72130f8737c0 100644 --- a/tests/ui/target_modifiers/no_value_bool.rs +++ b/tests/ui/target_modifiers/no_value_bool.rs @@ -8,7 +8,7 @@ //@ revisions: ok ok_explicit error error_explicit //@[ok] compile-flags: -Zreg-struct-return //@[ok_explicit] compile-flags: -Zreg-struct-return=true -//@[error] compile-flags: +// [error] no extra compile-flags //@[error_explicit] compile-flags: -Zreg-struct-return=false //@[ok] check-pass //@[ok_explicit] check-pass From 01891d55443fd602a8361ae8d47f9b5b1e4ec6c6 Mon Sep 17 00:00:00 2001 From: Kivooeo Date: Thu, 24 Jul 2025 21:26:50 +0500 Subject: [PATCH 187/201] remove hello world directory --- tests/ui/README.md | 4 ---- tests/ui/{hello_world/main.rs => warnings/hello-world.rs} | 0 2 files changed, 4 deletions(-) rename tests/ui/{hello_world/main.rs => warnings/hello-world.rs} (100%) diff --git a/tests/ui/README.md b/tests/ui/README.md index 86c9ad9c1ce8..66c1bb905a79 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -654,10 +654,6 @@ Tests on range patterns where one of the bounds is not a direct value. Tests for the standard library collection [`std::collections::HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html). -## `tests/ui/hello_world/` - -Tests that the basic hello-world program is not somehow broken. - ## `tests/ui/higher-ranked/` Tests for higher-ranked trait bounds. diff --git a/tests/ui/hello_world/main.rs b/tests/ui/warnings/hello-world.rs similarity index 100% rename from tests/ui/hello_world/main.rs rename to tests/ui/warnings/hello-world.rs From 05da623016f7bfed812261f2793b4b589631a6b0 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Sat, 26 Jul 2025 20:13:07 +0200 Subject: [PATCH 188/201] Fix Ord, Eq and Hash implementation of panic::Location Faster equality compare Add tests Add missing files for tests --- library/core/src/panic/location.rs | 42 +++++++++++++++++- library/coretests/tests/panic/location.rs | 43 ++++++++++++++++++- .../coretests/tests/panic/location/file_a.rs | 15 +++++++ .../coretests/tests/panic/location/file_b.rs | 15 +++++++ .../coretests/tests/panic/location/file_c.rs | 11 +++++ 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 library/coretests/tests/panic/location/file_a.rs create mode 100644 library/coretests/tests/panic/location/file_b.rs create mode 100644 library/coretests/tests/panic/location/file_c.rs diff --git a/library/core/src/panic/location.rs b/library/core/src/panic/location.rs index 972270208852..6ef7d5a22a30 100644 --- a/library/core/src/panic/location.rs +++ b/library/core/src/panic/location.rs @@ -1,5 +1,7 @@ +use crate::cmp::Ordering; use crate::ffi::CStr; use crate::fmt; +use crate::hash::{Hash, Hasher}; use crate::marker::PhantomData; use crate::ptr::NonNull; @@ -32,7 +34,7 @@ use crate::ptr::NonNull; /// Files are compared as strings, not `Path`, which could be unexpected. /// See [`Location::file`]'s documentation for more discussion. #[lang = "panic_location"] -#[derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[derive(Copy, Clone)] #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { // A raw pointer is used rather than a reference because the pointer is valid for one more byte @@ -44,6 +46,44 @@ pub struct Location<'a> { _filename: PhantomData<&'a str>, } +#[stable(feature = "panic_hooks", since = "1.10.0")] +impl PartialEq for Location<'_> { + fn eq(&self, other: &Self) -> bool { + // Compare col / line first as they're cheaper to compare and more likely to differ, + // while not impacting the result. + self.col == other.col && self.line == other.line && self.file() == other.file() + } +} + +#[stable(feature = "panic_hooks", since = "1.10.0")] +impl Eq for Location<'_> {} + +#[stable(feature = "panic_hooks", since = "1.10.0")] +impl Ord for Location<'_> { + fn cmp(&self, other: &Self) -> Ordering { + self.file() + .cmp(other.file()) + .then_with(|| self.line.cmp(&other.line)) + .then_with(|| self.col.cmp(&other.col)) + } +} + +#[stable(feature = "panic_hooks", since = "1.10.0")] +impl PartialOrd for Location<'_> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[stable(feature = "panic_hooks", since = "1.10.0")] +impl Hash for Location<'_> { + fn hash(&self, state: &mut H) { + self.file().hash(state); + self.line.hash(state); + self.col.hash(state); + } +} + #[stable(feature = "panic_hooks", since = "1.10.0")] impl fmt::Debug for Location<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/library/coretests/tests/panic/location.rs b/library/coretests/tests/panic/location.rs index 5ce0b06e90e1..910001bcc1c5 100644 --- a/library/coretests/tests/panic/location.rs +++ b/library/coretests/tests/panic/location.rs @@ -3,6 +3,23 @@ use core::panic::Location; // Note: Some of the following tests depend on the source location, // so please be careful when editing this file. +mod file_a; +mod file_b; +mod file_c; + +// A small shuffled set of locations for testing, along with their true order. +const LOCATIONS: [(usize, &'static Location<'_>); 9] = [ + (7, file_c::two()), + (0, file_a::one()), + (3, file_b::one()), + (5, file_b::three()), + (8, file_c::three()), + (6, file_c::one()), + (2, file_a::three()), + (4, file_b::two()), + (1, file_a::two()), +]; + #[test] fn location_const_caller() { const _CALLER_REFERENCE: &Location<'static> = Location::caller(); @@ -20,7 +37,7 @@ fn location_const_file() { fn location_const_line() { const CALLER: &Location<'static> = Location::caller(); const LINE: u32 = CALLER.line(); - assert_eq!(LINE, 21); + assert_eq!(LINE, 38); } #[test] @@ -34,6 +51,28 @@ fn location_const_column() { fn location_debug() { let f = format!("{:?}", Location::caller()); assert!(f.contains(&format!("{:?}", file!()))); - assert!(f.contains("35")); + assert!(f.contains("52")); assert!(f.contains("29")); } + +#[test] +fn location_eq() { + for (i, a) in LOCATIONS { + for (j, b) in LOCATIONS { + if i == j { + assert_eq!(a, b); + } else { + assert_ne!(a, b); + } + } + } +} + +#[test] +fn location_ord() { + let mut locations = LOCATIONS.clone(); + locations.sort_by_key(|(_o, l)| **l); + for (correct, (order, _l)) in locations.iter().enumerate() { + assert_eq!(correct, *order); + } +} diff --git a/library/coretests/tests/panic/location/file_a.rs b/library/coretests/tests/panic/location/file_a.rs new file mode 100644 index 000000000000..1ceb225ee9c3 --- /dev/null +++ b/library/coretests/tests/panic/location/file_a.rs @@ -0,0 +1,15 @@ +use core::panic::Location; + +// Used for test super::location_{ord, eq}. Must be in a dedicated file. + +pub const fn one() -> &'static Location<'static> { + Location::caller() +} + +pub const fn two() -> &'static Location<'static> { + Location::caller() +} + +pub const fn three() -> &'static Location<'static> { + Location::caller() +} diff --git a/library/coretests/tests/panic/location/file_b.rs b/library/coretests/tests/panic/location/file_b.rs new file mode 100644 index 000000000000..1ceb225ee9c3 --- /dev/null +++ b/library/coretests/tests/panic/location/file_b.rs @@ -0,0 +1,15 @@ +use core::panic::Location; + +// Used for test super::location_{ord, eq}. Must be in a dedicated file. + +pub const fn one() -> &'static Location<'static> { + Location::caller() +} + +pub const fn two() -> &'static Location<'static> { + Location::caller() +} + +pub const fn three() -> &'static Location<'static> { + Location::caller() +} diff --git a/library/coretests/tests/panic/location/file_c.rs b/library/coretests/tests/panic/location/file_c.rs new file mode 100644 index 000000000000..2ac416c19cf4 --- /dev/null +++ b/library/coretests/tests/panic/location/file_c.rs @@ -0,0 +1,11 @@ +// Used for test super::location_{ord, eq}. Must be in a dedicated file. + +// This is used for testing column ordering of Location, hence this ugly one-liner. +// We must fmt skip the entire containing module or else tidy will still complain. +#[rustfmt::skip] +mod no_fmt { + use core::panic::Location; + pub const fn one() -> &'static Location<'static> { Location::caller() } pub const fn two() -> &'static Location<'static> { Location::caller() } pub const fn three() -> &'static Location<'static> { Location::caller() } +} + +pub use no_fmt::*; From dee20136411854ec541561844eea6de73fe211fe Mon Sep 17 00:00:00 2001 From: Zalathar Date: Wed, 30 Jul 2025 12:28:16 +1000 Subject: [PATCH 189/201] compiletest: Move directive names back into a separate file This list no longer needs to be included in multiple crates, but having it in its own file makes it easier to find and update when necessary. --- src/tools/compiletest/src/directives.rs | 294 +----------------- .../src/directives/directive_names.rs | 289 +++++++++++++++++ 2 files changed, 293 insertions(+), 290 deletions(-) create mode 100644 src/tools/compiletest/src/directives/directive_names.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index a1f76a075562..646dbdae60fe 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -12,6 +12,9 @@ use tracing::*; use crate::common::{CodegenBackend, Config, Debugger, FailMode, PassMode, RunFailMode, TestMode}; use crate::debuggers::{extract_cdb_version, extract_gdb_version}; use crate::directives::auxiliary::{AuxProps, parse_and_update_aux}; +use crate::directives::directive_names::{ + KNOWN_DIRECTIVE_NAMES, KNOWN_HTMLDOCCK_DIRECTIVE_NAMES, KNOWN_JSONDOCCK_DIRECTIVE_NAMES, +}; use crate::directives::needs::CachedNeedsConditions; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldPanic}; @@ -20,6 +23,7 @@ use crate::util::static_regex; pub(crate) mod auxiliary; mod cfg; +mod directive_names; mod needs; #[cfg(test)] mod tests; @@ -769,296 +773,6 @@ fn line_directive<'line>( Some(DirectiveLine { line_number, revision, raw_directive }) } -/// This was originally generated by collecting directives from ui tests and then extracting their -/// directive names. This is **not** an exhaustive list of all possible directives. Instead, this is -/// a best-effort approximation for diagnostics. Add new directives to this list when needed. -const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ - // tidy-alphabetical-start - "add-core-stubs", - "assembly-output", - "aux-bin", - "aux-build", - "aux-codegen-backend", - "aux-crate", - "build-aux-docs", - "build-fail", - "build-pass", - "check-fail", - "check-pass", - "check-run-results", - "check-stdout", - "check-test-line-numbers-match", - "compile-flags", - "doc-flags", - "dont-check-compiler-stderr", - "dont-check-compiler-stdout", - "dont-check-failure-status", - "dont-require-annotations", - "edition", - "error-pattern", - "exact-llvm-major-version", - "exec-env", - "failure-status", - "filecheck-flags", - "forbid-output", - "force-host", - "ignore-16bit", - "ignore-32bit", - "ignore-64bit", - "ignore-aarch64", - "ignore-aarch64-pc-windows-msvc", - "ignore-aarch64-unknown-linux-gnu", - "ignore-aix", - "ignore-android", - "ignore-apple", - "ignore-arm", - "ignore-arm-unknown-linux-gnueabi", - "ignore-arm-unknown-linux-gnueabihf", - "ignore-arm-unknown-linux-musleabi", - "ignore-arm-unknown-linux-musleabihf", - "ignore-auxiliary", - "ignore-avr", - "ignore-backends", - "ignore-beta", - "ignore-cdb", - "ignore-compare-mode-next-solver", - "ignore-compare-mode-polonius", - "ignore-coverage-map", - "ignore-coverage-run", - "ignore-cross-compile", - "ignore-eabi", - "ignore-elf", - "ignore-emscripten", - "ignore-endian-big", - "ignore-enzyme", - "ignore-freebsd", - "ignore-fuchsia", - "ignore-gdb", - "ignore-gdb-version", - "ignore-gnu", - "ignore-haiku", - "ignore-horizon", - "ignore-i686-pc-windows-gnu", - "ignore-i686-pc-windows-msvc", - "ignore-illumos", - "ignore-ios", - "ignore-linux", - "ignore-lldb", - "ignore-llvm-version", - "ignore-loongarch32", - "ignore-loongarch64", - "ignore-macabi", - "ignore-macos", - "ignore-msp430", - "ignore-msvc", - "ignore-musl", - "ignore-netbsd", - "ignore-nightly", - "ignore-none", - "ignore-nto", - "ignore-nvptx64", - "ignore-nvptx64-nvidia-cuda", - "ignore-openbsd", - "ignore-pass", - "ignore-powerpc", - "ignore-powerpc64", - "ignore-remote", - "ignore-riscv64", - "ignore-rustc-debug-assertions", - "ignore-rustc_abi-x86-sse2", - "ignore-s390x", - "ignore-sgx", - "ignore-sparc64", - "ignore-spirv", - "ignore-stable", - "ignore-stage1", - "ignore-stage2", - "ignore-std-debug-assertions", - "ignore-test", - "ignore-thumb", - "ignore-thumbv8m.base-none-eabi", - "ignore-thumbv8m.main-none-eabi", - "ignore-tvos", - "ignore-unix", - "ignore-unknown", - "ignore-uwp", - "ignore-visionos", - "ignore-vxworks", - "ignore-wasi", - "ignore-wasm", - "ignore-wasm32", - "ignore-wasm32-bare", - "ignore-wasm64", - "ignore-watchos", - "ignore-windows", - "ignore-windows-gnu", - "ignore-windows-msvc", - "ignore-x32", - "ignore-x86", - "ignore-x86_64", - "ignore-x86_64-apple-darwin", - "ignore-x86_64-pc-windows-gnu", - "ignore-x86_64-unknown-linux-gnu", - "incremental", - "known-bug", - "llvm-cov-flags", - "max-llvm-major-version", - "min-cdb-version", - "min-gdb-version", - "min-lldb-version", - "min-llvm-version", - "min-system-llvm-version", - "needs-asm-support", - "needs-backends", - "needs-crate-type", - "needs-deterministic-layouts", - "needs-dlltool", - "needs-dynamic-linking", - "needs-enzyme", - "needs-force-clang-based-tests", - "needs-git-hash", - "needs-llvm-components", - "needs-llvm-zstd", - "needs-profiler-runtime", - "needs-relocation-model-pic", - "needs-run-enabled", - "needs-rust-lld", - "needs-rustc-debug-assertions", - "needs-sanitizer-address", - "needs-sanitizer-cfi", - "needs-sanitizer-dataflow", - "needs-sanitizer-hwaddress", - "needs-sanitizer-kcfi", - "needs-sanitizer-leak", - "needs-sanitizer-memory", - "needs-sanitizer-memtag", - "needs-sanitizer-safestack", - "needs-sanitizer-shadow-call-stack", - "needs-sanitizer-support", - "needs-sanitizer-thread", - "needs-std-debug-assertions", - "needs-subprocess", - "needs-symlink", - "needs-target-has-atomic", - "needs-target-std", - "needs-threads", - "needs-unwind", - "needs-wasmtime", - "needs-xray", - "no-auto-check-cfg", - "no-prefer-dynamic", - "normalize-stderr", - "normalize-stderr-32bit", - "normalize-stderr-64bit", - "normalize-stdout", - "only-16bit", - "only-32bit", - "only-64bit", - "only-aarch64", - "only-aarch64-apple-darwin", - "only-aarch64-unknown-linux-gnu", - "only-apple", - "only-arm", - "only-avr", - "only-beta", - "only-bpf", - "only-cdb", - "only-dist", - "only-elf", - "only-emscripten", - "only-gnu", - "only-i686-pc-windows-gnu", - "only-i686-pc-windows-msvc", - "only-i686-unknown-linux-gnu", - "only-ios", - "only-linux", - "only-loongarch32", - "only-loongarch64", - "only-loongarch64-unknown-linux-gnu", - "only-macos", - "only-mips", - "only-mips64", - "only-msp430", - "only-msvc", - "only-musl", - "only-nightly", - "only-nvptx64", - "only-powerpc", - "only-riscv64", - "only-rustc_abi-x86-sse2", - "only-s390x", - "only-sparc", - "only-sparc64", - "only-stable", - "only-thumb", - "only-tvos", - "only-uefi", - "only-unix", - "only-visionos", - "only-wasm32", - "only-wasm32-bare", - "only-wasm32-wasip1", - "only-watchos", - "only-windows", - "only-windows-gnu", - "only-windows-msvc", - "only-x86", - "only-x86_64", - "only-x86_64-apple-darwin", - "only-x86_64-fortanix-unknown-sgx", - "only-x86_64-pc-windows-gnu", - "only-x86_64-pc-windows-msvc", - "only-x86_64-unknown-linux-gnu", - "pp-exact", - "pretty-compare-only", - "pretty-mode", - "proc-macro", - "reference", - "regex-error-pattern", - "remap-src-base", - "revisions", - "run-crash", - "run-fail", - "run-fail-or-crash", - "run-flags", - "run-pass", - "run-rustfix", - "rustc-env", - "rustfix-only-machine-applicable", - "should-fail", - "should-ice", - "stderr-per-bitwidth", - "test-mir-pass", - "unique-doc-out-dir", - "unset-exec-env", - "unset-rustc-env", - // Used by the tidy check `unknown_revision`. - "unused-revision-names", - // tidy-alphabetical-end -]; - -const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[ - "count", - "!count", - "files", - "!files", - "has", - "!has", - "has-dir", - "!has-dir", - "hasraw", - "!hasraw", - "matches", - "!matches", - "matchesraw", - "!matchesraw", - "snapshot", - "!snapshot", -]; - -const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = - &["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"]; - /// The (partly) broken-down contents of a line containing a test directive, /// which [`iter_directives`] passes to its callback function. /// diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs new file mode 100644 index 000000000000..7fc76a42e0ca --- /dev/null +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -0,0 +1,289 @@ +/// This was originally generated by collecting directives from ui tests and then extracting their +/// directive names. This is **not** an exhaustive list of all possible directives. Instead, this is +/// a best-effort approximation for diagnostics. Add new directives to this list when needed. +pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ + // tidy-alphabetical-start + "add-core-stubs", + "assembly-output", + "aux-bin", + "aux-build", + "aux-codegen-backend", + "aux-crate", + "build-aux-docs", + "build-fail", + "build-pass", + "check-fail", + "check-pass", + "check-run-results", + "check-stdout", + "check-test-line-numbers-match", + "compile-flags", + "doc-flags", + "dont-check-compiler-stderr", + "dont-check-compiler-stdout", + "dont-check-failure-status", + "dont-require-annotations", + "edition", + "error-pattern", + "exact-llvm-major-version", + "exec-env", + "failure-status", + "filecheck-flags", + "forbid-output", + "force-host", + "ignore-16bit", + "ignore-32bit", + "ignore-64bit", + "ignore-aarch64", + "ignore-aarch64-pc-windows-msvc", + "ignore-aarch64-unknown-linux-gnu", + "ignore-aix", + "ignore-android", + "ignore-apple", + "ignore-arm", + "ignore-arm-unknown-linux-gnueabi", + "ignore-arm-unknown-linux-gnueabihf", + "ignore-arm-unknown-linux-musleabi", + "ignore-arm-unknown-linux-musleabihf", + "ignore-auxiliary", + "ignore-avr", + "ignore-backends", + "ignore-beta", + "ignore-cdb", + "ignore-compare-mode-next-solver", + "ignore-compare-mode-polonius", + "ignore-coverage-map", + "ignore-coverage-run", + "ignore-cross-compile", + "ignore-eabi", + "ignore-elf", + "ignore-emscripten", + "ignore-endian-big", + "ignore-enzyme", + "ignore-freebsd", + "ignore-fuchsia", + "ignore-gdb", + "ignore-gdb-version", + "ignore-gnu", + "ignore-haiku", + "ignore-horizon", + "ignore-i686-pc-windows-gnu", + "ignore-i686-pc-windows-msvc", + "ignore-illumos", + "ignore-ios", + "ignore-linux", + "ignore-lldb", + "ignore-llvm-version", + "ignore-loongarch32", + "ignore-loongarch64", + "ignore-macabi", + "ignore-macos", + "ignore-msp430", + "ignore-msvc", + "ignore-musl", + "ignore-netbsd", + "ignore-nightly", + "ignore-none", + "ignore-nto", + "ignore-nvptx64", + "ignore-nvptx64-nvidia-cuda", + "ignore-openbsd", + "ignore-pass", + "ignore-powerpc", + "ignore-powerpc64", + "ignore-remote", + "ignore-riscv64", + "ignore-rustc-debug-assertions", + "ignore-rustc_abi-x86-sse2", + "ignore-s390x", + "ignore-sgx", + "ignore-sparc64", + "ignore-spirv", + "ignore-stable", + "ignore-stage1", + "ignore-stage2", + "ignore-std-debug-assertions", + "ignore-test", + "ignore-thumb", + "ignore-thumbv8m.base-none-eabi", + "ignore-thumbv8m.main-none-eabi", + "ignore-tvos", + "ignore-unix", + "ignore-unknown", + "ignore-uwp", + "ignore-visionos", + "ignore-vxworks", + "ignore-wasi", + "ignore-wasm", + "ignore-wasm32", + "ignore-wasm32-bare", + "ignore-wasm64", + "ignore-watchos", + "ignore-windows", + "ignore-windows-gnu", + "ignore-windows-msvc", + "ignore-x32", + "ignore-x86", + "ignore-x86_64", + "ignore-x86_64-apple-darwin", + "ignore-x86_64-pc-windows-gnu", + "ignore-x86_64-unknown-linux-gnu", + "incremental", + "known-bug", + "llvm-cov-flags", + "max-llvm-major-version", + "min-cdb-version", + "min-gdb-version", + "min-lldb-version", + "min-llvm-version", + "min-system-llvm-version", + "needs-asm-support", + "needs-backends", + "needs-crate-type", + "needs-deterministic-layouts", + "needs-dlltool", + "needs-dynamic-linking", + "needs-enzyme", + "needs-force-clang-based-tests", + "needs-git-hash", + "needs-llvm-components", + "needs-llvm-zstd", + "needs-profiler-runtime", + "needs-relocation-model-pic", + "needs-run-enabled", + "needs-rust-lld", + "needs-rustc-debug-assertions", + "needs-sanitizer-address", + "needs-sanitizer-cfi", + "needs-sanitizer-dataflow", + "needs-sanitizer-hwaddress", + "needs-sanitizer-kcfi", + "needs-sanitizer-leak", + "needs-sanitizer-memory", + "needs-sanitizer-memtag", + "needs-sanitizer-safestack", + "needs-sanitizer-shadow-call-stack", + "needs-sanitizer-support", + "needs-sanitizer-thread", + "needs-std-debug-assertions", + "needs-subprocess", + "needs-symlink", + "needs-target-has-atomic", + "needs-target-std", + "needs-threads", + "needs-unwind", + "needs-wasmtime", + "needs-xray", + "no-auto-check-cfg", + "no-prefer-dynamic", + "normalize-stderr", + "normalize-stderr-32bit", + "normalize-stderr-64bit", + "normalize-stdout", + "only-16bit", + "only-32bit", + "only-64bit", + "only-aarch64", + "only-aarch64-apple-darwin", + "only-aarch64-unknown-linux-gnu", + "only-apple", + "only-arm", + "only-avr", + "only-beta", + "only-bpf", + "only-cdb", + "only-dist", + "only-elf", + "only-emscripten", + "only-gnu", + "only-i686-pc-windows-gnu", + "only-i686-pc-windows-msvc", + "only-i686-unknown-linux-gnu", + "only-ios", + "only-linux", + "only-loongarch32", + "only-loongarch64", + "only-loongarch64-unknown-linux-gnu", + "only-macos", + "only-mips", + "only-mips64", + "only-msp430", + "only-msvc", + "only-musl", + "only-nightly", + "only-nvptx64", + "only-powerpc", + "only-riscv64", + "only-rustc_abi-x86-sse2", + "only-s390x", + "only-sparc", + "only-sparc64", + "only-stable", + "only-thumb", + "only-tvos", + "only-uefi", + "only-unix", + "only-visionos", + "only-wasm32", + "only-wasm32-bare", + "only-wasm32-wasip1", + "only-watchos", + "only-windows", + "only-windows-gnu", + "only-windows-msvc", + "only-x86", + "only-x86_64", + "only-x86_64-apple-darwin", + "only-x86_64-fortanix-unknown-sgx", + "only-x86_64-pc-windows-gnu", + "only-x86_64-pc-windows-msvc", + "only-x86_64-unknown-linux-gnu", + "pp-exact", + "pretty-compare-only", + "pretty-mode", + "proc-macro", + "reference", + "regex-error-pattern", + "remap-src-base", + "revisions", + "run-crash", + "run-fail", + "run-fail-or-crash", + "run-flags", + "run-pass", + "run-rustfix", + "rustc-env", + "rustfix-only-machine-applicable", + "should-fail", + "should-ice", + "stderr-per-bitwidth", + "test-mir-pass", + "unique-doc-out-dir", + "unset-exec-env", + "unset-rustc-env", + // Used by the tidy check `unknown_revision`. + "unused-revision-names", + // tidy-alphabetical-end +]; + +pub(crate) const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[ + "count", + "!count", + "files", + "!files", + "has", + "!has", + "has-dir", + "!has-dir", + "hasraw", + "!hasraw", + "matches", + "!matches", + "matchesraw", + "!matchesraw", + "snapshot", + "!snapshot", +]; + +pub(crate) const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] = + &["count", "!count", "has", "!has", "is", "!is", "ismany", "!ismany", "set", "!set"]; From 98d08ff014840a15244374178e8b9ed02da3d4ca Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 30 Jul 2025 03:39:16 +0000 Subject: [PATCH 190/201] Make sure to account for the right item universal regions in borrowck --- .../rustc_borrowck/src/universal_regions.rs | 27 ++++++++++++++----- .../liberated-region-from-outer-closure.rs | 12 +++++++++ ...liberated-region-from-outer-closure.stderr | 17 ++++++++++++ .../escape-argument-callee.stderr | 3 +++ .../escape-argument.stderr | 2 ++ ...pagate-approximated-fail-no-postdom.stderr | 2 ++ .../propagate-approximated-ref.stderr | 6 +++++ ...er-to-static-comparing-against-free.stderr | 2 ++ ...oximated-shorter-to-static-no-bound.stderr | 5 ++++ ...mated-shorter-to-static-wrong-bound.stderr | 6 +++++ .../propagate-approximated-val.stderr | 2 ++ .../propagate-despite-same-free-region.stderr | 2 ++ ...ail-to-approximate-longer-no-bounds.stderr | 5 ++++ ...-to-approximate-longer-wrong-bounds.stderr | 6 +++++ .../return-wrong-bound-region.stderr | 2 ++ ...ram-closure-approximate-lower-bound.stderr | 4 +++ 16 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 tests/ui/borrowck/liberated-region-from-outer-closure.rs create mode 100644 tests/ui/borrowck/liberated-region-from-outer-closure.stderr diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs index f138f2653203..240c9a5223be 100644 --- a/compiler/rustc_borrowck/src/universal_regions.rs +++ b/compiler/rustc_borrowck/src/universal_regions.rs @@ -969,13 +969,28 @@ fn for_each_late_bound_region_in_item<'tcx>( mir_def_id: LocalDefId, mut f: impl FnMut(ty::Region<'tcx>), ) { - if !tcx.def_kind(mir_def_id).is_fn_like() { - return; - } + let bound_vars = match tcx.def_kind(mir_def_id) { + DefKind::Fn | DefKind::AssocFn => { + tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)) + } + // We extract the bound vars from the deduced closure signature, since we may have + // only deduced that a param in the closure signature is late-bound from a constraint + // that we discover during typeck. + DefKind::Closure => { + let ty = tcx.type_of(mir_def_id).instantiate_identity(); + match *ty.kind() { + ty::Closure(_, args) => args.as_closure().sig().bound_vars(), + ty::CoroutineClosure(_, args) => { + args.as_coroutine_closure().coroutine_closure_sig().bound_vars() + } + ty::Coroutine(_, _) | ty::Error(_) => return, + _ => unreachable!("unexpected type for closure: {ty}"), + } + } + _ => return, + }; - for (idx, bound_var) in - tcx.late_bound_vars(tcx.local_def_id_to_hir_id(mir_def_id)).iter().enumerate() - { + for (idx, bound_var) in bound_vars.iter().enumerate() { if let ty::BoundVariableKind::Region(kind) = bound_var { let kind = ty::LateParamRegionKind::from_bound(ty::BoundVar::from_usize(idx), kind); let liberated_region = ty::Region::new_late_param(tcx, mir_def_id.to_def_id(), kind); diff --git a/tests/ui/borrowck/liberated-region-from-outer-closure.rs b/tests/ui/borrowck/liberated-region-from-outer-closure.rs new file mode 100644 index 000000000000..dcc6370b4a12 --- /dev/null +++ b/tests/ui/borrowck/liberated-region-from-outer-closure.rs @@ -0,0 +1,12 @@ +// Regression test for . + +fn example(x: T) -> impl FnMut(&mut ()) { + move |_: &mut ()| { + move || needs_static_lifetime(x); + //~^ ERROR the parameter type `T` may not live long enough + } +} + +fn needs_static_lifetime(obj: T) {} + +fn main() {} diff --git a/tests/ui/borrowck/liberated-region-from-outer-closure.stderr b/tests/ui/borrowck/liberated-region-from-outer-closure.stderr new file mode 100644 index 000000000000..98b45ac499dc --- /dev/null +++ b/tests/ui/borrowck/liberated-region-from-outer-closure.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/liberated-region-from-outer-closure.rs:5:17 + | +LL | move || needs_static_lifetime(x); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | fn example(x: T) -> impl FnMut(&mut ()) { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr index a445534c8d80..2742162c8211 100644 --- a/tests/ui/nll/closure-requirements/escape-argument-callee.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument-callee.stderr @@ -9,6 +9,9 @@ LL | let mut closure = expect_sig(|p, y| *p = y); for extern "rust-call" fn((&'^0 mut &'^1 i32, &'^2 i32)), (), ] + = note: late-bound region is '?1 + = note: late-bound region is '?2 + = note: late-bound region is '?3 error: lifetime may not live long enough --> $DIR/escape-argument-callee.rs:26:45 diff --git a/tests/ui/nll/closure-requirements/escape-argument.stderr b/tests/ui/nll/closure-requirements/escape-argument.stderr index 7fd1cd8c3e42..22cb0367ad87 100644 --- a/tests/ui/nll/closure-requirements/escape-argument.stderr +++ b/tests/ui/nll/closure-requirements/escape-argument.stderr @@ -9,6 +9,8 @@ LL | let mut closure = expect_sig(|p, y| *p = y); for extern "rust-call" fn((&'^0 mut &'^1 i32, &'^1 i32)), (), ] + = note: late-bound region is '?1 + = note: late-bound region is '?2 note: no external requirements --> $DIR/escape-argument.rs:20:1 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr index 60087ec992bd..134ce99014d8 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr @@ -9,6 +9,8 @@ LL | |_outlives1, _outlives2, _outlives3, x, y| { for extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'?2 &'^0 u32>, std::cell::Cell<&'^1 &'?3 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)), (), ] + = note: late-bound region is '?7 + = note: late-bound region is '?8 = note: late-bound region is '?4 = note: late-bound region is '?5 = note: late-bound region is '?6 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr index 7325a9de8b29..f5527eeb2cdb 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-ref.stderr @@ -9,6 +9,12 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y for extern "rust-call" fn((&'^0 std::cell::Cell<&'?1 &'^1 u32>, &'^2 std::cell::Cell<&'^3 &'?2 u32>, &'^4 std::cell::Cell<&'^1 u32>, &'^5 std::cell::Cell<&'^3 u32>)), (), ] + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: late-bound region is '?7 + = note: late-bound region is '?8 + = note: late-bound region is '?9 + = note: late-bound region is '?10 = note: late-bound region is '?3 = note: late-bound region is '?4 = note: number of external vids: 5 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr index 621c1ea083b2..e13653f34234 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr @@ -9,6 +9,7 @@ LL | foo(cell, |cell_a, cell_x| { for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&'^0 u32>)), (), ] + = note: late-bound region is '?2 error[E0521]: borrowed data escapes outside of closure --> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:22:9 @@ -43,6 +44,7 @@ LL | foo(cell, |cell_a, cell_x| { for extern "rust-call" fn((std::cell::Cell<&'?1 u32>, std::cell::Cell<&'^0 u32>)), (), ] + = note: late-bound region is '?2 = note: number of external vids: 2 = note: where '?1: '?0 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr index b9365c94a1be..9e9eae985973 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-no-bound.stderr @@ -9,6 +9,11 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { for extern "rust-call" fn((&'^0 std::cell::Cell<&'?1 &'^1 u32>, &'^2 std::cell::Cell<&'^1 u32>, &'^3 std::cell::Cell<&'^4 u32>)), (), ] + = note: late-bound region is '?4 + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: late-bound region is '?7 + = note: late-bound region is '?8 = note: late-bound region is '?2 = note: late-bound region is '?3 = note: number of external vids: 4 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr index e5d2867103cf..303fcd4cdfcf 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-wrong-bound.stderr @@ -9,6 +9,12 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y for extern "rust-call" fn((&'^0 std::cell::Cell<&'?1 &'^1 u32>, &'^2 std::cell::Cell<&'?2 &'^3 u32>, &'^4 std::cell::Cell<&'^1 u32>, &'^5 std::cell::Cell<&'^3 u32>)), (), ] + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: late-bound region is '?7 + = note: late-bound region is '?8 + = note: late-bound region is '?9 + = note: late-bound region is '?10 = note: late-bound region is '?3 = note: late-bound region is '?4 = note: number of external vids: 5 diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr index a14bfb06e831..aa75b4c811c1 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-val.stderr @@ -9,6 +9,8 @@ LL | establish_relationships(cell_a, cell_b, |outlives1, outlives2, x, y| { for extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'^1 &'?2 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)), (), ] + = note: late-bound region is '?5 + = note: late-bound region is '?6 = note: late-bound region is '?3 = note: late-bound region is '?4 = note: number of external vids: 5 diff --git a/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr b/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr index 49c65d77ddde..30ee259d3dce 100644 --- a/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr +++ b/tests/ui/nll/closure-requirements/propagate-despite-same-free-region.stderr @@ -9,6 +9,8 @@ LL | |_outlives1, _outlives2, x, y| { for extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'^1 &'?2 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)), (), ] + = note: late-bound region is '?4 + = note: late-bound region is '?5 = note: late-bound region is '?3 = note: number of external vids: 4 = note: where '?1: '?2 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr index f48ed2823ddb..6b04e346c697 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr @@ -9,6 +9,11 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| { for extern "rust-call" fn((&'^0 std::cell::Cell<&'^1 &'?1 u32>, &'^2 std::cell::Cell<&'^3 u32>, &'^4 std::cell::Cell<&'^1 u32>)), (), ] + = note: late-bound region is '?4 + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: late-bound region is '?7 + = note: late-bound region is '?8 = note: late-bound region is '?2 = note: late-bound region is '?3 diff --git a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr index a090e94593fe..ae2129c65f2c 100644 --- a/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr @@ -9,6 +9,12 @@ LL | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y for extern "rust-call" fn((&'^0 std::cell::Cell<&'^1 &'?1 u32>, &'^2 std::cell::Cell<&'^3 &'?2 u32>, &'^4 std::cell::Cell<&'^1 u32>, &'^5 std::cell::Cell<&'^3 u32>)), (), ] + = note: late-bound region is '?5 + = note: late-bound region is '?6 + = note: late-bound region is '?7 + = note: late-bound region is '?8 + = note: late-bound region is '?9 + = note: late-bound region is '?10 = note: late-bound region is '?3 = note: late-bound region is '?4 diff --git a/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr b/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr index bc5c04a27a3e..1f1cce1e8854 100644 --- a/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr +++ b/tests/ui/nll/closure-requirements/return-wrong-bound-region.stderr @@ -9,6 +9,8 @@ LL | expect_sig(|a, b| b); // ought to return `a` for extern "rust-call" fn((&'^0 i32, &'^1 i32)) -> &'^0 i32, (), ] + = note: late-bound region is '?1 + = note: late-bound region is '?2 error: lifetime may not live long enough --> $DIR/return-wrong-bound-region.rs:11:23 diff --git a/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr b/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr index e58764354c03..396e149554ce 100644 --- a/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr +++ b/tests/ui/nll/ty-outlives/ty-param-closure-approximate-lower-bound.stderr @@ -9,6 +9,8 @@ LL | twice(cell, value, |a, b| invoke(a, b)); for extern "rust-call" fn((std::option::Option>, &'^1 T)), (), ] + = note: late-bound region is '?2 + = note: late-bound region is '?3 = note: number of external vids: 2 = note: where T: '?1 @@ -31,6 +33,8 @@ LL | twice(cell, value, |a, b| invoke(a, b)); for extern "rust-call" fn((std::option::Option>, &'^1 T)), (), ] + = note: late-bound region is '?3 + = note: late-bound region is '?4 = note: late-bound region is '?2 = note: number of external vids: 3 = note: where T: '?1 From 43018d3722254cfea4ce553309d8909f8b5aaa2d Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 22 Jul 2025 13:22:00 +0000 Subject: [PATCH 191/201] Free disk space on Windows 2025 runners --- .github/workflows/ci.yml | 9 +- src/ci/github-actions/jobs.yml | 13 +- src/ci/scripts/free-disk-space-linux.sh | 265 ++++++++++++++++++++ src/ci/scripts/free-disk-space-windows.ps1 | 35 +++ src/ci/scripts/free-disk-space.sh | 268 +-------------------- 5 files changed, 316 insertions(+), 274 deletions(-) create mode 100755 src/ci/scripts/free-disk-space-linux.sh create mode 100644 src/ci/scripts/free-disk-space-windows.ps1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dc8ac539a3a0..ac73bcabb977 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -117,7 +117,7 @@ jobs: with: fetch-depth: 2 - # Free up disk space on Linux by removing preinstalled components that + # Free up disk space on Linux and Windows by removing preinstalled components that # we do not need. We do this to enable some of the less resource # intensive jobs to run on free runners, which however also have # less disk space. @@ -125,6 +125,13 @@ jobs: run: src/ci/scripts/free-disk-space.sh if: matrix.free_disk + # If we don't need to free up disk space then just report how much space we have + - name: print disk usage + run: | + echo "disk usage:" + df -h + if: matrix.free_disk == false + # Rust Log Analyzer can't currently detect the PR number of a GitHub # Actions build on its own, so a hint in the log message is needed to # point it in the right direction. diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 0a6ebe44b3d7..541272dc9375 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -31,20 +31,11 @@ runners: <<: *base-job - &job-windows - os: windows-2022 - <<: *base-job - - # NOTE: windows-2025 has less disk space available than windows-2022, - # because the D drive is missing. - - &job-windows-25 os: windows-2025 + free_disk: true <<: *base-job - &job-windows-8c - os: windows-2022-8core-32gb - <<: *base-job - - - &job-windows-25-8c os: windows-2025-8core-32gb <<: *base-job @@ -655,7 +646,7 @@ auto: SCRIPT: python x.py build --set rust.debug=true opt-dist && PGO_HOST=x86_64-pc-windows-msvc ./build/x86_64-pc-windows-msvc/stage0-tools-bin/opt-dist windows-ci -- python x.py dist bootstrap --include-default-paths DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift - <<: *job-windows-25-8c + <<: *job-windows-8c - name: dist-i686-msvc env: diff --git a/src/ci/scripts/free-disk-space-linux.sh b/src/ci/scripts/free-disk-space-linux.sh new file mode 100755 index 000000000000..32649fe0d9b3 --- /dev/null +++ b/src/ci/scripts/free-disk-space-linux.sh @@ -0,0 +1,265 @@ +#!/bin/bash +set -euo pipefail + +# Free disk space on Linux GitHub action runners +# Script inspired by https://github.com/jlumbroso/free-disk-space + +isX86() { + local arch + arch=$(uname -m) + if [ "$arch" = "x86_64" ]; then + return 0 + else + return 1 + fi +} + +# Check if we're on a GitHub hosted runner. +# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". +isGitHubRunner() { + # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". + if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then + return 0 + else + return 1 + fi +} + +# print a line of the specified character +printSeparationLine() { + for ((i = 0; i < 80; i++)); do + printf "%s" "$1" + done + printf "\n" +} + +# compute available space +# REF: https://unix.stackexchange.com/a/42049/60849 +# REF: https://stackoverflow.com/a/450821/408734 +getAvailableSpace() { + df -a | awk 'NR > 1 {avail+=$4} END {print avail}' +} + +# make Kb human readable (assume the input is Kb) +# REF: https://unix.stackexchange.com/a/44087/60849 +formatByteCount() { + numfmt --to=iec-i --suffix=B --padding=7 "${1}000" +} + +# macro to output saved space +printSavedSpace() { + # Disk space before the operation + local before=${1} + local title=${2:-} + + local after + after=$(getAvailableSpace) + local saved=$((after - before)) + + if [ "$saved" -lt 0 ]; then + echo "::warning::Saved space is negative: $saved. Using '0' as saved space." + saved=0 + fi + + echo "" + printSeparationLine "*" + if [ -n "${title}" ]; then + echo "=> ${title}: Saved $(formatByteCount "$saved")" + else + echo "=> Saved $(formatByteCount "$saved")" + fi + printSeparationLine "*" + echo "" +} + +# macro to print output of df with caption +printDF() { + local caption=${1} + + printSeparationLine "=" + echo "${caption}" + echo "" + echo "$ df -h" + echo "" + df -h + printSeparationLine "=" +} + +removeUnusedFilesAndDirs() { + local to_remove=( + "/usr/share/java" + ) + + if isGitHubRunner; then + to_remove+=( + "/usr/local/aws-sam-cli" + "/usr/local/doc/cmake" + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/cmake-"* + "/usr/local/share/edge_driver" + "/usr/local/share/emacs" + "/usr/local/share/gecko_driver" + "/usr/local/share/icons" + "/usr/local/share/powershell" + "/usr/local/share/vcpkg" + "/usr/local/share/vim" + "/usr/share/apache-maven-"* + "/usr/share/gradle-"* + "/usr/share/kotlinc" + "/usr/share/miniconda" + "/usr/share/php" + "/usr/share/ri" + "/usr/share/swift" + + # binaries + "/usr/local/bin/azcopy" + "/usr/local/bin/bicep" + "/usr/local/bin/ccmake" + "/usr/local/bin/cmake-"* + "/usr/local/bin/cmake" + "/usr/local/bin/cpack" + "/usr/local/bin/ctest" + "/usr/local/bin/helm" + "/usr/local/bin/kind" + "/usr/local/bin/kustomize" + "/usr/local/bin/minikube" + "/usr/local/bin/packer" + "/usr/local/bin/phpunit" + "/usr/local/bin/pulumi-"* + "/usr/local/bin/pulumi" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" + + # Azure + "/opt/az" + "/usr/share/az_"* + ) + + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then + # Environment variable set by GitHub Actions + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + else + echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." + fi + else + # Remove folders and files present in AWS CodeBuild + to_remove+=( + # binaries + "/usr/local/bin/ecs-cli" + "/usr/local/bin/eksctl" + "/usr/local/bin/kubectl" + + "${HOME}/.gradle" + "${HOME}/.dotnet" + "${HOME}/.goenv" + "${HOME}/.phpenv" + + ) + fi + + for element in "${to_remove[@]}"; do + if [ ! -e "$element" ]; then + # The file or directory doesn't exist. + # Maybe it was removed in a newer version of the runner or it's not present in a + # specific architecture (e.g. ARM). + echo "::warning::Directory or file $element does not exist, skipping." + fi + done + + # Remove all files and directories at once to save time. + sudo rm -rf "${to_remove[@]}" +} + +execAndMeasureSpaceChange() { + local operation=${1} # Function to execute + local title=${2} + + local before + before=$(getAvailableSpace) + $operation + + printSavedSpace "$before" "$title" +} + +# Remove large packages +# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh +cleanPackages() { + local packages=( + '^aspnetcore-.*' + '^dotnet-.*' + '^llvm-.*' + '^mongodb-.*' + 'firefox' + 'libgl1-mesa-dri' + 'mono-devel' + 'php.*' + ) + + if isGitHubRunner; then + packages+=( + azure-cli + ) + + if isX86; then + packages+=( + 'google-chrome-stable' + 'google-cloud-cli' + 'google-cloud-sdk' + 'powershell' + ) + fi + else + packages+=( + 'google-chrome-stable' + ) + fi + + sudo apt-get -qq remove -y --fix-missing "${packages[@]}" + + sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" + sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" +} + +# Remove Docker images. +# Ubuntu 22 runners have docker images already installed. +# They aren't present in ubuntu 24 runners. +cleanDocker() { + echo "=> Removing the following docker images:" + sudo docker image ls + echo "=> Removing docker images..." + sudo docker image prune --all --force || true +} + +# Remove Swap storage +cleanSwap() { + sudo swapoff -a || true + sudo rm -rf /mnt/swapfile || true + free -h +} + +# Display initial disk space stats + +AVAILABLE_INITIAL=$(getAvailableSpace) + +printDF "BEFORE CLEAN-UP:" +echo "" +execAndMeasureSpaceChange cleanPackages "Unused packages" +execAndMeasureSpaceChange cleanDocker "Docker images" +execAndMeasureSpaceChange cleanSwap "Swap storage" +execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories" + +# Output saved space statistic +echo "" +printDF "AFTER CLEAN-UP:" + +echo "" +echo "" + +printSavedSpace "$AVAILABLE_INITIAL" "Total saved" diff --git a/src/ci/scripts/free-disk-space-windows.ps1 b/src/ci/scripts/free-disk-space-windows.ps1 new file mode 100644 index 000000000000..8a4677bd2ab4 --- /dev/null +++ b/src/ci/scripts/free-disk-space-windows.ps1 @@ -0,0 +1,35 @@ +# Free disk space on Windows GitHub action runners. + +$ErrorActionPreference = 'Stop' + +Get-Volume | Out-String | Write-Output + +$available = $(Get-Volume C).SizeRemaining + +$dirs = 'C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm', +'C:\rtools45', 'C:\ghcup', 'C:\Program Files (x86)\Android', +'C:\Program Files\Google\Chrome', 'C:\Program Files (x86)\Microsoft\Edge', +'C:\Program Files\Mozilla Firefox', 'C:\Program Files\MySQL', 'C:\Julia', +'C:\Program Files\MongoDB', 'C:\Program Files\Azure Cosmos DB Emulator', +'C:\Program Files\PostgreSQL', 'C:\Program Files\Unity Hub', +'C:\Strawberry', 'C:\hostedtoolcache\windows\Java_Temurin-Hotspot_jdk' + +foreach ($dir in $dirs) { + Start-ThreadJob -InputObject $dir { + Remove-Item -Recurse -Force -LiteralPath $input + } | Out-Null +} + +foreach ($job in Get-Job) { + Wait-Job $job | Out-Null + if ($job.Error) { + Write-Output "::warning file=$PSCommandPath::$($job.Error)" + } + Remove-Job $job +} + +Get-Volume | Out-String | Write-Output + +$saved = ($(Get-Volume C).SizeRemaining - $available) / 1gb +$savedRounded = [math]::Round($saved, 3) +Write-Output "total space saved: $savedRounded GB" diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index 173f64858b37..062ad801cd8d 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -1,266 +1,10 @@ #!/bin/bash set -euo pipefail -# Free disk space on Linux GitHub action runners -# Script inspired by https://github.com/jlumbroso/free-disk-space +script_dir=$(dirname "$0") -isX86() { - local arch - arch=$(uname -m) - if [ "$arch" = "x86_64" ]; then - return 0 - else - return 1 - fi -} - -# Check if we're on a GitHub hosted runner. -# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted". -isGitHubRunner() { - # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string". - if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then - return 0 - else - return 1 - fi -} - -# print a line of the specified character -printSeparationLine() { - for ((i = 0; i < 80; i++)); do - printf "%s" "$1" - done - printf "\n" -} - -# compute available space -# REF: https://unix.stackexchange.com/a/42049/60849 -# REF: https://stackoverflow.com/a/450821/408734 -getAvailableSpace() { - df -a | awk 'NR > 1 {avail+=$4} END {print avail}' -} - -# make Kb human readable (assume the input is Kb) -# REF: https://unix.stackexchange.com/a/44087/60849 -formatByteCount() { - numfmt --to=iec-i --suffix=B --padding=7 "${1}000" -} - -# macro to output saved space -printSavedSpace() { - # Disk space before the operation - local before=${1} - local title=${2:-} - - local after - after=$(getAvailableSpace) - local saved=$((after - before)) - - if [ "$saved" -lt 0 ]; then - echo "::warning::Saved space is negative: $saved. Using '0' as saved space." - saved=0 - fi - - echo "" - printSeparationLine "*" - if [ -n "${title}" ]; then - echo "=> ${title}: Saved $(formatByteCount "$saved")" - else - echo "=> Saved $(formatByteCount "$saved")" - fi - printSeparationLine "*" - echo "" -} - -# macro to print output of df with caption -printDF() { - local caption=${1} - - printSeparationLine "=" - echo "${caption}" - echo "" - echo "$ df -h" - echo "" - df -h - printSeparationLine "=" -} - -removeUnusedFilesAndDirs() { - local to_remove=( - "/usr/share/java" - ) - - if isGitHubRunner; then - to_remove+=( - "/usr/local/aws-sam-cli" - "/usr/local/doc/cmake" - "/usr/local/julia"* - "/usr/local/lib/android" - "/usr/local/share/chromedriver-"* - "/usr/local/share/chromium" - "/usr/local/share/cmake-"* - "/usr/local/share/edge_driver" - "/usr/local/share/emacs" - "/usr/local/share/gecko_driver" - "/usr/local/share/icons" - "/usr/local/share/powershell" - "/usr/local/share/vcpkg" - "/usr/local/share/vim" - "/usr/share/apache-maven-"* - "/usr/share/gradle-"* - "/usr/share/kotlinc" - "/usr/share/miniconda" - "/usr/share/php" - "/usr/share/ri" - "/usr/share/swift" - - # binaries - "/usr/local/bin/azcopy" - "/usr/local/bin/bicep" - "/usr/local/bin/ccmake" - "/usr/local/bin/cmake-"* - "/usr/local/bin/cmake" - "/usr/local/bin/cpack" - "/usr/local/bin/ctest" - "/usr/local/bin/helm" - "/usr/local/bin/kind" - "/usr/local/bin/kustomize" - "/usr/local/bin/minikube" - "/usr/local/bin/packer" - "/usr/local/bin/phpunit" - "/usr/local/bin/pulumi-"* - "/usr/local/bin/pulumi" - "/usr/local/bin/stack" - - # Haskell runtime - "/usr/local/.ghcup" - - # Azure - "/opt/az" - "/usr/share/az_"* - ) - - if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then - # Environment variable set by GitHub Actions - to_remove+=( - "${AGENT_TOOLSDIRECTORY}" - ) - else - echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." - fi - else - # Remove folders and files present in AWS CodeBuild - to_remove+=( - # binaries - "/usr/local/bin/ecs-cli" - "/usr/local/bin/eksctl" - "/usr/local/bin/kubectl" - - "${HOME}/.gradle" - "${HOME}/.dotnet" - "${HOME}/.goenv" - "${HOME}/.phpenv" - - ) - fi - - for element in "${to_remove[@]}"; do - if [ ! -e "$element" ]; then - # The file or directory doesn't exist. - # Maybe it was removed in a newer version of the runner or it's not present in a - # specific architecture (e.g. ARM). - echo "::warning::Directory or file $element does not exist, skipping." - fi - done - - # Remove all files and directories at once to save time. - sudo rm -rf "${to_remove[@]}" -} - -execAndMeasureSpaceChange() { - local operation=${1} # Function to execute - local title=${2} - - local before - before=$(getAvailableSpace) - $operation - - printSavedSpace "$before" "$title" -} - -# Remove large packages -# REF: https://github.com/apache/flink/blob/master/tools/azure-pipelines/free_disk_space.sh -cleanPackages() { - local packages=( - '^aspnetcore-.*' - '^dotnet-.*' - '^llvm-.*' - '^mongodb-.*' - 'firefox' - 'libgl1-mesa-dri' - 'mono-devel' - 'php.*' - ) - - if isGitHubRunner; then - packages+=( - azure-cli - ) - - if isX86; then - packages+=( - 'google-chrome-stable' - 'google-cloud-cli' - 'google-cloud-sdk' - 'powershell' - ) - fi - else - packages+=( - 'google-chrome-stable' - ) - fi - - sudo apt-get -qq remove -y --fix-missing "${packages[@]}" - - sudo apt-get autoremove -y || echo "::warning::The command [sudo apt-get autoremove -y] failed" - sudo apt-get clean || echo "::warning::The command [sudo apt-get clean] failed failed" -} - -# Remove Docker images. -# Ubuntu 22 runners have docker images already installed. -# They aren't present in ubuntu 24 runners. -cleanDocker() { - echo "=> Removing the following docker images:" - sudo docker image ls - echo "=> Removing docker images..." - sudo docker image prune --all --force || true -} - -# Remove Swap storage -cleanSwap() { - sudo swapoff -a || true - sudo rm -rf /mnt/swapfile || true - free -h -} - -# Display initial disk space stats - -AVAILABLE_INITIAL=$(getAvailableSpace) - -printDF "BEFORE CLEAN-UP:" -echo "" - -execAndMeasureSpaceChange cleanPackages "Unused packages" -execAndMeasureSpaceChange cleanDocker "Docker images" -execAndMeasureSpaceChange cleanSwap "Swap storage" -execAndMeasureSpaceChange removeUnusedFilesAndDirs "Unused files and directories" - -# Output saved space statistic -echo "" -printDF "AFTER CLEAN-UP:" - -echo "" -echo "" - -printSavedSpace "$AVAILABLE_INITIAL" "Total saved" +if [[ "${RUNNER_OS:-}" == "Windows" ]]; then + pwsh $script_dir/free-disk-space-windows.ps1 +else + $script_dir/free-disk-space-linux.sh +fi From 21af1549988d7194b9daeb6882a5582a980d892e Mon Sep 17 00:00:00 2001 From: David Tenty Date: Wed, 30 Jul 2025 00:39:43 -0400 Subject: [PATCH 192/201] [test][run-make] add needs-llvm-components Add some constraints to run-make tests that require specific target support and will fail without them. --- tests/run-make/link-cfg/rmake.rs | 1 + tests/run-make/mismatching-target-triples/rmake.rs | 1 + tests/run-make/musl-default-linking/rmake.rs | 1 + tests/run-make/rustdoc-target-spec-json-path/rmake.rs | 1 + tests/run-make/target-specs/rmake.rs | 1 + 5 files changed, 5 insertions(+) diff --git a/tests/run-make/link-cfg/rmake.rs b/tests/run-make/link-cfg/rmake.rs index 732de5dbd0b8..18577fb836dd 100644 --- a/tests/run-make/link-cfg/rmake.rs +++ b/tests/run-make/link-cfg/rmake.rs @@ -12,6 +12,7 @@ //@ ignore-cross-compile // Reason: the compiled binary is executed +//@ needs-llvm-components: x86 use run_make_support::{bare_rustc, build_native_dynamic_lib, build_native_static_lib, run, rustc}; diff --git a/tests/run-make/mismatching-target-triples/rmake.rs b/tests/run-make/mismatching-target-triples/rmake.rs index 6f41eac8cdaf..1bbe945e0da5 100644 --- a/tests/run-make/mismatching-target-triples/rmake.rs +++ b/tests/run-make/mismatching-target-triples/rmake.rs @@ -4,6 +4,7 @@ // now replaced by a clearer normal error message. This test checks that this aforementioned // error message is used. // See https://github.com/rust-lang/rust/issues/10814 +//@ needs-llvm-components: x86 use run_make_support::rustc; diff --git a/tests/run-make/musl-default-linking/rmake.rs b/tests/run-make/musl-default-linking/rmake.rs index 017444cfcddc..7bb54e2739c9 100644 --- a/tests/run-make/musl-default-linking/rmake.rs +++ b/tests/run-make/musl-default-linking/rmake.rs @@ -4,6 +4,7 @@ use run_make_support::{rustc, serde_json}; // Per https://github.com/rust-lang/compiler-team/issues/422, // we should be trying to move these targets to dynamically link // musl libc by default. +//@ needs-llvm-components: aarch64 arm mips powerpc riscv systemz x86 static LEGACY_STATIC_LINKING_TARGETS: &[&'static str] = &[ "aarch64-unknown-linux-musl", "arm-unknown-linux-musleabi", diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs index fe9587f50222..d43aa9b90ac7 100644 --- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs +++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs @@ -1,4 +1,5 @@ // Test that rustdoc will properly canonicalize the target spec json path just like rustc. +//@ needs-llvm-components: x86 use run_make_support::{cwd, rustc, rustdoc}; diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs index 9184e5f772f7..398f6bddc91b 100644 --- a/tests/run-make/target-specs/rmake.rs +++ b/tests/run-make/target-specs/rmake.rs @@ -4,6 +4,7 @@ // with the target flag's bundle of new features to check that compilation either succeeds while // using them correctly, or fails with the right error message when using them improperly. // See https://github.com/rust-lang/rust/pull/16156 +//@ needs-llvm-components: x86 use run_make_support::{diff, rfs, rustc}; From 0cf6eb5f7521d37213576e8b48b574defa1e49f0 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 30 Jul 2025 19:54:16 +0800 Subject: [PATCH 193/201] Introduce proper `build.compiletest-allow-stage0` config option In favor of the adhoc `COMPILETEST_FORCE_STAGE0` env var. --- bootstrap.example.toml | 5 +++++ src/bootstrap/src/core/build_steps/test.rs | 11 +++++------ src/bootstrap/src/core/config/config.rs | 13 +++++++++++++ src/bootstrap/src/core/config/toml/build.rs | 1 + 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/bootstrap.example.toml b/bootstrap.example.toml index ef49113b70f4..31966af33012 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -465,6 +465,11 @@ # What custom diff tool to use for displaying compiletest tests. #build.compiletest-diff-tool = +# Whether to allow `compiletest` self-tests and `compiletest`-managed test +# suites to be run against the stage 0 rustc. This is only intended to be used +# when the stage 0 compiler is actually built from in-tree sources. +#build.compiletest-allow-stage0 = false + # Whether to use the precompiled stage0 libtest with compiletest. #build.compiletest-use-stage0-libtest = true diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 0f9268097d7b..3f62f8b20138 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -723,8 +723,8 @@ impl Step for CompiletestTest { let mut cargo = tool::prepare_tool_cargo( builder, compiler, - // compiletest uses libtest internals; make it use the in-tree std to make sure it never breaks - // when std sources change. + // compiletest uses libtest internals; make it use the in-tree std to make sure it never + // breaks when std sources change. Mode::ToolStd, host, Kind::Test, @@ -1612,12 +1612,11 @@ impl Step for Compiletest { return; } - if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() { + if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 { eprintln!("\ ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail -HELP: to test the compiler, use `--stage 1` instead -HELP: to test the standard library, use `--stage 0 library/std` instead -NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`." +HELP: to test the compiler or standard library, omit the stage or explicitly use `--stage 1` instead +NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`." ); crate::exit!(1); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 9644ade00b34..78abdd7f9b8c 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -298,8 +298,16 @@ pub struct Config { /// Command for visual diff display, e.g. `diff-tool --color=always`. pub compiletest_diff_tool: Option, + /// Whether to allow running both `compiletest` self-tests and `compiletest`-managed test suites + /// against the stage 0 (rustc, std). + /// + /// This is only intended to be used when the stage 0 compiler is actually built from in-tree + /// sources. + pub compiletest_allow_stage0: bool, + /// Whether to use the precompiled stage0 libtest with compiletest. pub compiletest_use_stage0_libtest: bool, + /// Default value for `--extra-checks` pub tidy_extra_checks: Option, pub is_running_on_ci: bool, @@ -749,6 +757,7 @@ impl Config { optimized_compiler_builtins, jobs, compiletest_diff_tool, + compiletest_allow_stage0, compiletest_use_stage0_libtest, tidy_extra_checks, ccache, @@ -1020,8 +1029,12 @@ impl Config { config.optimized_compiler_builtins = optimized_compiler_builtins.unwrap_or(config.channel != "dev"); + config.compiletest_diff_tool = compiletest_diff_tool; + + config.compiletest_allow_stage0 = compiletest_allow_stage0.unwrap_or(false); config.compiletest_use_stage0_libtest = compiletest_use_stage0_libtest.unwrap_or(true); + config.tidy_extra_checks = tidy_extra_checks; let download_rustc = config.download_rustc_commit.is_some(); diff --git a/src/bootstrap/src/core/config/toml/build.rs b/src/bootstrap/src/core/config/toml/build.rs index 4d29691f38b6..728367b39729 100644 --- a/src/bootstrap/src/core/config/toml/build.rs +++ b/src/bootstrap/src/core/config/toml/build.rs @@ -68,6 +68,7 @@ define_config! { optimized_compiler_builtins: Option = "optimized-compiler-builtins", jobs: Option = "jobs", compiletest_diff_tool: Option = "compiletest-diff-tool", + compiletest_allow_stage0: Option = "compiletest-allow-stage0", compiletest_use_stage0_libtest: Option = "compiletest-use-stage0-libtest", tidy_extra_checks: Option = "tidy-extra-checks", ccache: Option = "ccache", From a7fcc738c98d04ea5b504719555478becbba3dce Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 30 Jul 2025 16:27:33 +0800 Subject: [PATCH 194/201] Update `codegen_{cranelift,gcc}` and `opt-dist` to use `build.compiletest-allow-stage0` --- .../rustc_codegen_cranelift/scripts/setup_rust_fork.sh | 1 + .../rustc_codegen_cranelift/scripts/test_rustc_tests.sh | 2 +- compiler/rustc_codegen_gcc/build_system/src/test.rs | 7 ++++--- src/tools/opt-dist/src/tests.rs | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh index 532702bb1a46..492f4dc44527 100644 --- a/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh +++ b/compiler/rustc_codegen_cranelift/scripts/setup_rust_fork.sh @@ -33,6 +33,7 @@ rustc = "$(pwd)/../dist/bin/rustc-clif" cargo = "$(rustup which cargo)" full-bootstrap = true local-rebuild = true +compiletest-allow-stage0 = true [rust] download-rustc = false diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh index 7e356b4b462b..52e02c857c7a 100755 --- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh +++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh @@ -166,5 +166,5 @@ index 073116933bd..c3e4578204d 100644 EOF echo "[TEST] rustc test suite" -COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} +./x.py test --stage 0 --test-args=--no-capture tests/{codegen-units,run-make,ui,incremental} popd diff --git a/compiler/rustc_codegen_gcc/build_system/src/test.rs b/compiler/rustc_codegen_gcc/build_system/src/test.rs index bc0fdd40b6e8..2c8271c36a94 100644 --- a/compiler/rustc_codegen_gcc/build_system/src/test.rs +++ b/compiler/rustc_codegen_gcc/build_system/src/test.rs @@ -561,8 +561,6 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc asm test suite"); - env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); - let codegen_backend_path = format!( "{pwd}/target/{channel}/librustc_codegen_gcc.{dylib_ext}", pwd = std::env::current_dir() @@ -588,6 +586,8 @@ fn asm_tests(env: &Env, args: &TestArg) -> Result<(), String> { &"always", &"--stage", &"0", + &"--set", + &"build.compiletest-allow-stage0=true", &"tests/assembly-llvm/asm", &"--compiletest-rustc-args", &rustc_args, @@ -1047,7 +1047,6 @@ where // FIXME: create a function "display_if_not_quiet" or something along the line. println!("[TEST] rustc {test_type} test suite"); - env.insert("COMPILETEST_FORCE_STAGE0".to_string(), "1".to_string()); let extra = if args.is_using_gcc_master_branch() { "" } else { " -Csymbol-mangling-version=v0" }; @@ -1070,6 +1069,8 @@ where &"always", &"--stage", &"0", + &"--set", + &"build.compiletest-allow-stage0=true", &format!("tests/{test_type}"), &"--compiletest-rustc-args", &rustc_args, diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs index c9a21fc6fb29..d5121b8c7869 100644 --- a/src/tools/opt-dist/src/tests.rs +++ b/src/tools/opt-dist/src/tests.rs @@ -79,6 +79,7 @@ lld = false rustc = "{rustc}" cargo = "{cargo}" local-rebuild = true +compiletest-allow-stage0=true [target.{host_triple}] llvm-config = "{llvm_config}" @@ -117,7 +118,6 @@ llvm-config = "{llvm_config}" args.extend(["--skip", test_path]); } cmd(&args) - .env("COMPILETEST_FORCE_STAGE0", "1") // Also run dist-only tests .env("COMPILETEST_ENABLE_DIST_TESTS", "1") .run() From 2033a06d7fb1970794998331dd8747e19caaa6e7 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 30 Jul 2025 19:54:47 +0800 Subject: [PATCH 195/201] Deny `compiletest` self-tests being run against stage 0 rustc unless explicitly allowed Otherwise, `compiletest` would have to know e.g. how to parse two different target spec, if target spec format was changed between beta `rustc` and in-tree `rustc`. --- src/bootstrap/src/core/build_steps/test.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 3f62f8b20138..951ca73fcc49 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -8,6 +8,9 @@ use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::{env, fs, iter}; +#[cfg(feature = "tracing")] +use tracing::instrument; + use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::DocumentationFormat; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; @@ -30,7 +33,7 @@ use crate::utils::helpers::{ linker_flags, t, target_supports_cranelift_backend, up_to_date, }; use crate::utils::render_tests::{add_flags_and_try_run_tests, try_run_tests}; -use crate::{CLang, DocTests, GitRepo, Mode, PathSet, envify}; +use crate::{CLang, DocTests, GitRepo, Mode, PathSet, debug, envify}; const ADB_TEST_DIR: &str = "/data/local/tmp/work"; @@ -713,9 +716,23 @@ impl Step for CompiletestTest { } /// Runs `cargo test` for compiletest. + #[cfg_attr( + feature = "tracing", + instrument(level = "debug", name = "CompiletestTest::run", skip_all) + )] fn run(self, builder: &Builder<'_>) { let host = self.host; + + if builder.top_stage == 0 && !builder.config.compiletest_allow_stage0 { + eprintln!("\ +ERROR: `--stage 0` runs compiletest self-tests against the stage0 (precompiled) compiler, not the in-tree compiler, and will almost always cause tests to fail +NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `--set build.compiletest-allow-stage0=true`." + ); + crate::exit!(1); + } + let compiler = builder.compiler(builder.top_stage, host); + debug!(?compiler); // We need `ToolStd` for the locally-built sysroot because // compiletest uses unstable features of the `test` crate. From 2cdd19384ff21a6a4f8377e7ec9aab43b7c9f675 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 30 Jul 2025 16:21:17 +0800 Subject: [PATCH 196/201] Run `compiletest` self-tests against stage 1 rustc And move `./x test compiletest --stage=1` to `pr-check-2`, where testing library artifacts already requires building the stage 1 compiler. --- src/ci/docker/host-x86_64/pr-check-1/Dockerfile | 1 - src/ci/docker/host-x86_64/pr-check-2/Dockerfile | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile index f7d51fba861e..04ac0f33daf0 100644 --- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile @@ -43,7 +43,6 @@ ENV SCRIPT \ python3 ../x.py check bootstrap && \ /scripts/check-default-config-profiles.sh && \ python3 ../x.py build src/tools/build-manifest && \ - python3 ../x.py test --stage 0 src/tools/compiletest && \ python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \ python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \ python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \ diff --git a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile index ce18a181d313..f82e19bcbb47 100644 --- a/src/ci/docker/host-x86_64/pr-check-2/Dockerfile +++ b/src/ci/docker/host-x86_64/pr-check-2/Dockerfile @@ -30,6 +30,7 @@ ENV SCRIPT \ python3 ../x.py check && \ python3 ../x.py clippy ci && \ python3 ../x.py test --stage 1 core alloc std test proc_macro && \ + python3 ../x.py test --stage 1 src/tools/compiletest && \ python3 ../x.py doc --stage 0 bootstrap && \ # Build both public and internal documentation. RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \ @@ -37,6 +38,6 @@ ENV SCRIPT \ mkdir -p /checkout/obj/staging/doc && \ cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \ RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library/test && \ - # The BOOTSTRAP_TRACING flag is added to verify whether the + # The BOOTSTRAP_TRACING flag is added to verify whether the # bootstrap process compiles successfully with this flag enabled. BOOTSTRAP_TRACING=1 python3 ../x.py --help From e954253d43d361845c248860e4aa521b90bf6443 Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Wed, 30 Jul 2025 16:24:32 +0800 Subject: [PATCH 197/201] Add change tracker entry --- src/bootstrap/src/utils/change_tracker.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 4b0c48213648..d3331b81587e 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -486,4 +486,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Warning, summary: "Removed `rust.description` and `llvm.ccache` as it was deprecated in #137723 and #136941 long time ago.", }, + ChangeInfo { + change_id: 144675, + severity: ChangeSeverity::Warning, + summary: "Added `build.compiletest-allow-stage0` flag instead of `COMPILETEST_FORCE_STAGE0` env var, and reject running `compiletest` self tests against stage 0 rustc unless explicitly allowed.", + }, ]; From b6cbe33aeb526d6437304f4810762c947bddcd4a Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 25 Jul 2025 12:38:54 +0000 Subject: [PATCH 198/201] handle region dependent goals due to infer vars --- compiler/rustc_hir_typeck/src/lib.rs | 17 +++++- compiler/rustc_infer/src/infer/mod.rs | 57 +++++++++++++------ .../src/infer/outlives/obligations.rs | 1 + .../src/infer/snapshot/undo_log.rs | 8 ++- .../src/solve/fulfill.rs | 12 +++- ...iguity-due-to-uniquification-3.next.stderr | 19 +++++++ .../ambiguity-due-to-uniquification-3.rs | 33 +++++++++++ 7 files changed, 128 insertions(+), 19 deletions(-) create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr create mode 100644 tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9e324286fa17..b395aa8162c3 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi}; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc}; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::{bug, span_bug}; use rustc_session::config; use rustc_span::Span; @@ -259,6 +259,21 @@ fn typeck_with_inspect<'tcx>( let typeck_results = fcx.resolve_type_vars_in_body(body); + // Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`. + if let None = fcx.infcx.tainted_by_errors() { + for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() { + let obligation = fcx.resolve_vars_if_possible(obligation); + if obligation.has_non_region_infer() { + bug!("unexpected inference variable after writeback: {obligation:?}"); + } + fcx.register_predicate(obligation); + } + fcx.select_obligations_where_possible(|_| {}); + if let None = fcx.infcx.tainted_by_errors() { + fcx.report_ambiguity_errors(); + } + } + fcx.detect_opaque_types_added_during_writeback(); // Consistency check our TypeckResults instance can hold all ItemLocalIds diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 17c587c5e7fd..5bbd8a84b7f0 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -37,10 +37,11 @@ use snapshot::undo_log::InferCtxtUndoLogs; use tracing::{debug, instrument}; use type_variable::TypeVariableOrigin; -use crate::infer::region_constraints::UndoLog; +use crate::infer::snapshot::undo_log::UndoLog; use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey}; use crate::traits::{ - self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine, + self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations, + TraitEngine, }; pub mod at; @@ -156,6 +157,12 @@ pub struct InferCtxtInner<'tcx> { /// which may cause types to no longer be considered well-formed. region_assumptions: Vec>, + /// `-Znext-solver`: Successfully proven goals during HIR typeck which + /// reference inference variables and get reproven after writeback. + /// + /// See the documentation of `InferCtxt::in_hir_typeck` for more details. + hir_typeck_potentially_region_dependent_goals: Vec>, + /// Caches for opaque type inference. opaque_type_storage: OpaqueTypeStorage<'tcx>, } @@ -173,6 +180,7 @@ impl<'tcx> InferCtxtInner<'tcx> { region_constraint_storage: Some(Default::default()), region_obligations: Default::default(), region_assumptions: Default::default(), + hir_typeck_potentially_region_dependent_goals: Default::default(), opaque_type_storage: Default::default(), } } @@ -247,24 +255,25 @@ pub struct InferCtxt<'tcx> { /// the root universe. Most notably, this is used during HIR typeck as region /// solving is left to borrowck instead. pub considering_regions: bool, - /// Whether this inference context is used by HIR typeck. If so, we uniquify regions - /// with `-Znext-solver`. This is necessary as borrowck will start by replacing each - /// occurance of a free region with a unique inference variable so if HIR typeck - /// ends up depending on two regions being equal we'd get unexpected mismatches - /// between HIR typeck and MIR typeck, resulting in an ICE. + /// `-Znext-solver`: Whether this inference context is used by HIR typeck. If so, we + /// need to make sure we don't rely on region identity in the trait solver or when + /// relating types. This is necessary as borrowck starts by replacing each occurrence of a + /// free region with a unique inference variable. If HIR typeck ends up depending on two + /// regions being equal we'd get unexpected mismatches between HIR typeck and MIR typeck, + /// resulting in an ICE. /// /// The trait solver sometimes depends on regions being identical. As a concrete example /// the trait solver ignores other candidates if one candidate exists without any constraints. - /// The goal `&'a u32: Equals<&'a u32>` has no constraints right now, but if we replace - /// each occurance of `'a` with a unique region the goal now equates these regions. + /// The goal `&'a u32: Equals<&'a u32>` has no constraints right now. If we replace each + /// occurrence of `'a` with a unique region the goal now equates these regions. See + /// the tests in trait-system-refactor-initiative#27 for concrete examples. /// - /// See the tests in trait-system-refactor-initiative#27 for concrete examples. - /// - /// FIXME(-Znext-solver): This is insufficient in theory as a goal `T: Trait` - /// may rely on the two occurances of `?x` being identical. If `?x` gets inferred to a - /// type containing regions, this will no longer be the case. We can handle this case - /// by storing goals which hold while still depending on inference vars and then - /// reproving them before writeback. + /// We handle this by *uniquifying* region when canonicalizing root goals during HIR typeck. + /// This is still insufficient as inference variables may *hide* region variables, so e.g. + /// `dyn TwoSuper: Super` may hold but MIR typeck could end up having to prove + /// `dyn TwoSuper<&'0 (), &'1 ()>: Super<&'2 ()>` which is now ambiguous. Because of this we + /// stash all successfully proven goals which reference inference variables and then reprove + /// them after writeback. pub in_hir_typeck: bool, /// If set, this flag causes us to skip the 'leak check' during @@ -1010,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> { } } + pub fn push_hir_typeck_potentially_region_dependent_goal( + &self, + goal: PredicateObligation<'tcx>, + ) { + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushHirTypeckPotentiallyRegionDependentGoal); + inner.hir_typeck_potentially_region_dependent_goals.push(goal); + } + + pub fn take_hir_typeck_potentially_region_dependent_goals( + &self, + ) -> Vec> { + assert!(!self.in_snapshot(), "cannot take goals in a snapshot"); + std::mem::take(&mut self.inner.borrow_mut().hir_typeck_potentially_region_dependent_goals) + } + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { self.resolve_vars_if_possible(t).to_string() } diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs index a8520c0e71dd..bb3b51c0ab2c 100644 --- a/compiler/rustc_infer/src/infer/outlives/obligations.rs +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -177,6 +177,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn take_registered_region_assumptions(&self) -> Vec> { + assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot"); std::mem::take(&mut self.inner.borrow_mut().region_assumptions) } diff --git a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs index 40e4c329446d..fcc0ab3af410 100644 --- a/compiler/rustc_infer/src/infer/snapshot/undo_log.rs +++ b/compiler/rustc_infer/src/infer/snapshot/undo_log.rs @@ -29,6 +29,7 @@ pub(crate) enum UndoLog<'tcx> { ProjectionCache(traits::UndoLog<'tcx>), PushTypeOutlivesConstraint, PushRegionAssumption, + PushHirTypeckPotentiallyRegionDependentGoal, } macro_rules! impl_from { @@ -79,7 +80,12 @@ impl<'tcx> Rollback> for InferCtxtInner<'tcx> { assert_matches!(popped, Some(_), "pushed region constraint but could not pop it"); } UndoLog::PushRegionAssumption => { - self.region_assumptions.pop(); + let popped = self.region_assumptions.pop(); + assert_matches!(popped, Some(_), "pushed region assumption but could not pop it"); + } + UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => { + let popped = self.hir_typeck_potentially_region_dependent_goals.pop(); + assert_matches!(popped, Some(_), "pushed goal but could not pop it"); } } } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 70452947a8b5..e0b9380eca32 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -197,6 +197,12 @@ where delegate.compute_goal_fast_path(goal, obligation.cause.span) { match certainty { + // This fast path doesn't depend on region identity so it doesn't + // matter if the goal contains inference variables or not, so we + // don't need to call `push_hir_typeck_potentially_region_dependent_goal` + // here. + // + // Only goals proven via the trait solver should be region dependent. Certainty::Yes => {} Certainty::Maybe(_) => { self.obligations.register(obligation, None); @@ -234,7 +240,11 @@ where } match certainty { - Certainty::Yes => {} + Certainty::Yes => { + if infcx.in_hir_typeck && obligation.has_non_region_infer() { + infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); + } + } Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on), } } diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr new file mode 100644 index 000000000000..e25f892b3657 --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.next.stderr @@ -0,0 +1,19 @@ +error[E0283]: type annotations needed: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` + --> $DIR/ambiguity-due-to-uniquification-3.rs:28:17 + | +LL | impls_trait(obj, t); + | ----------- ^^^ + | | + | required by a bound introduced by this call + | + = note: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>` + = help: the trait `Trait` is implemented for `()` +note: required by a bound in `impls_trait` + --> $DIR/ambiguity-due-to-uniquification-3.rs:24:19 + | +LL | fn impls_trait, U>(_: Inv, _: Inv) {} + | ^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs new file mode 100644 index 000000000000..6dcd9d5bdf4d --- /dev/null +++ b/tests/ui/traits/next-solver/assembly/ambiguity-due-to-uniquification-3.rs @@ -0,0 +1,33 @@ +//@ revisions: current next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[current] check-pass + +// Regression test from trait-system-refactor-initiative#27. +// +// Unlike in the previous two tests, `dyn Object: Trait` relies +// on structural identity of type inference variables. This inference variable +// gets constrained to a type containing a region later on. To prevent this +// from causing an ICE during MIR borrowck, we stash goals which depend on +// inference variables and then reprove them at the end of HIR typeck. + +#![feature(rustc_attrs)] +#![rustc_no_implicit_bounds] +trait Trait {} +impl Trait for () {} + +trait Object: Trait + Trait {} + +#[derive(Clone, Copy)] +struct Inv(*mut T); +fn foo() -> (Inv>, Inv) { todo!() } +fn impls_trait, U>(_: Inv, _: Inv) {} + +fn bar() { + let (obj, t) = foo(); + impls_trait(obj, t); + //[next]~^ ERROR type annotations needed + let _: Inv> = obj; +} + +fn main() {} From df2e54376c4cf9009ba159a50a404e1483f52f67 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 30 Jul 2025 11:57:41 +0200 Subject: [PATCH 199/201] add comment and opaque type fixme --- compiler/rustc_trait_selection/src/solve/fulfill.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index e0b9380eca32..01bdae7435d7 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -241,6 +241,17 @@ where match certainty { Certainty::Yes => { + // Goals may depend on structural identity. Region uniquification at the + // start of MIR borrowck may cause things to no longer be so, potentially + // causing an ICE. + // + // While we uniquify root goals in HIR this does not handle cases where + // regions are hidden inside of a type or const inference variable. + // + // FIXME(-Znext-solver): This does not handle inference variables hidden + // inside of an opaque type, e.g. if there's `Opaque = (?x, ?x)` in the + // storage, we can also rely on structural identity of `?x` even if we + // later uniquify it in MIR borrowck. if infcx.in_hir_typeck && obligation.has_non_region_infer() { infcx.push_hir_typeck_potentially_region_dependent_goal(obligation); } From 2b065e7c0b453cc6de5e89bbab47df3bc4212940 Mon Sep 17 00:00:00 2001 From: lcnr Date: Wed, 30 Jul 2025 14:30:57 +0200 Subject: [PATCH 200/201] extend comment --- compiler/rustc_next_trait_solver/src/solve/mod.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 9d52dee3c659..aec9594b834b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -389,6 +389,19 @@ pub struct GoalEvaluation { /// The goal we've evaluated. This is the input goal, but potentially with its /// inference variables resolved. This never applies any inference constraints /// from evaluating the goal. + /// + /// We rely on this to check whether root goals in HIR typeck had an unresolved + /// type inference variable in the input. We must not resolve this after evaluating + /// the goal as even if the inference variable has been resolved by evaluating the + /// goal itself, this goal may still end up failing due to region uniquification + /// later on. + /// + /// This is used as a minor optimization to avoid re-resolving inference variables + /// when reevaluating ambiguous goals. E.g. if we've got a goal `?x: Trait` with `?x` + /// already being constrained to `Vec`, then the first evaluation resolves it to + /// `Vec: Trait`. If this goal is still ambiguous and we later resolve `?y` to `u32`, + /// then reevaluating this goal now only needs to resolve `?y` while it would otherwise + /// have to resolve both `?x` and `?y`, pub goal: Goal, pub certainty: Certainty, pub has_changed: HasChanged, From 1d589c87bef43ea685927fc97d3c8b8fff725184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Tue, 29 Jul 2025 23:55:04 +0200 Subject: [PATCH 201/201] clean up codegen fn attrs --- .../rustc_codegen_ssa/src/codegen_attrs.rs | 564 ++++++++++-------- 1 file changed, 308 insertions(+), 256 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index e187331c696c..5c54bce6e035 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -4,12 +4,12 @@ use rustc_abi::{Align, ExternAbi}; use rustc_ast::expand::autodiff_attrs::{AutoDiffAttrs, DiffActivity, DiffMode}; use rustc_ast::{LitKind, MetaItem, MetaItemInner, attr}; use rustc_attr_data_structures::{ - AttributeKind, InlineAttr, InstructionSetAttr, OptimizeAttr, UsedBy, find_attr, + AttributeKind, InlineAttr, InstructionSetAttr, UsedBy, find_attr, }; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS; -use rustc_hir::{self as hir, LangItem, lang_items}; +use rustc_hir::{self as hir, Attribute, LangItem, lang_items}; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, }; @@ -53,77 +53,196 @@ fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage { } } -fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { - if cfg!(debug_assertions) { - let def_kind = tcx.def_kind(did); - assert!( - def_kind.has_codegen_attrs(), - "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", - ); +/// In some cases, attributes are only valid on functions, but it's the `check_attr` +/// pass that checks that they aren't used anywhere else, rather than this module. +/// In these cases, we bail from performing further checks that are only meaningful for +/// functions (such as calling `fn_sig`, which ICEs if given a non-function). We also +/// report a delayed bug, just in case `check_attr` isn't doing its job. +fn try_fn_sig<'tcx>( + tcx: TyCtxt<'tcx>, + did: LocalDefId, + attr_span: Span, +) -> Option>> { + use DefKind::*; + + let def_kind = tcx.def_kind(did); + if let Fn | AssocFn | Variant | Ctor(..) = def_kind { + Some(tcx.fn_sig(did)) + } else { + tcx.dcx().span_delayed_bug(attr_span, "this attribute can only be applied to functions"); + None + } +} + +// FIXME(jdonszelmann): remove when instruction_set becomes a parsed attr +fn parse_instruction_set_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option { + let list = attr.meta_item_list()?; + + match &list[..] { + [MetaItemInner::MetaItem(set)] => { + let segments = set.path.segments.iter().map(|x| x.ident.name).collect::>(); + match segments.as_slice() { + [sym::arm, sym::a32 | sym::t32] if !tcx.sess.target.has_thumb_interworking => { + tcx.dcx().emit_err(errors::UnsupportedInstructionSet { span: attr.span() }); + None + } + [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32), + [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32), + _ => { + tcx.dcx().emit_err(errors::InvalidInstructionSet { span: attr.span() }); + None + } + } + } + [] => { + tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() }); + None + } + _ => { + tcx.dcx().emit_err(errors::MultipleInstructionSet { span: attr.span() }); + None + } + } +} + +// FIXME(jdonszelmann): remove when linkage becomes a parsed attr +fn parse_linkage_attr(tcx: TyCtxt<'_>, did: LocalDefId, attr: &Attribute) -> Option { + let val = attr.value_str()?; + let linkage = linkage_by_name(tcx, did, val.as_str()); + Some(linkage) +} + +// FIXME(jdonszelmann): remove when no_sanitize becomes a parsed attr +fn parse_no_sanitize_attr(tcx: TyCtxt<'_>, attr: &Attribute) -> Option { + let list = attr.meta_item_list()?; + let mut sanitizer_set = SanitizerSet::empty(); + + for item in list.iter() { + match item.name() { + Some(sym::address) => { + sanitizer_set |= SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS + } + Some(sym::cfi) => sanitizer_set |= SanitizerSet::CFI, + Some(sym::kcfi) => sanitizer_set |= SanitizerSet::KCFI, + Some(sym::memory) => sanitizer_set |= SanitizerSet::MEMORY, + Some(sym::memtag) => sanitizer_set |= SanitizerSet::MEMTAG, + Some(sym::shadow_call_stack) => sanitizer_set |= SanitizerSet::SHADOWCALLSTACK, + Some(sym::thread) => sanitizer_set |= SanitizerSet::THREAD, + Some(sym::hwaddress) => sanitizer_set |= SanitizerSet::HWADDRESS, + _ => { + tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); + } + } } - let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)); - let mut codegen_fn_attrs = CodegenFnAttrs::new(); - if tcx.should_inherit_track_caller(did) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; - } + Some(sanitizer_set) +} + +// FIXME(jdonszelmann): remove when patchable_function_entry becomes a parsed attr +fn parse_patchable_function_entry( + tcx: TyCtxt<'_>, + attr: &Attribute, +) -> Option { + attr.meta_item_list().and_then(|l| { + let mut prefix = None; + let mut entry = None; + for item in l { + let Some(meta_item) = item.meta_item() else { + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); + continue; + }; + + let Some(name_value_lit) = meta_item.name_value_literal() else { + tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); + continue; + }; + + let attrib_to_write = match meta_item.name() { + Some(sym::prefix_nops) => &mut prefix, + Some(sym::entry_nops) => &mut entry, + _ => { + tcx.dcx().emit_err(errors::UnexpectedParameterName { + span: item.span(), + prefix_nops: sym::prefix_nops, + entry_nops: sym::entry_nops, + }); + continue; + } + }; + + let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { + tcx.dcx().emit_err(errors::InvalidLiteralValue { span: name_value_lit.span }); + continue; + }; + + let Ok(val) = val.get().try_into() else { + tcx.dcx().emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); + continue; + }; + + *attrib_to_write = Some(val); + } + + if let (None, None) = (prefix, entry) { + tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); + } + + Some(PatchableFunctionEntry::from_prefix_and_entry(prefix.unwrap_or(0), entry.unwrap_or(0))) + }) +} + +/// Spans that are collected when processing built-in attributes, +/// that are useful for emitting diagnostics later. +#[derive(Default)] +struct InterestingAttributeDiagnosticSpans { + link_ordinal: Option, + no_sanitize: Option, + inline: Option, + no_mangle: Option, +} + +/// Process the builtin attrs ([`hir::Attribute`]) on the item. +/// Many of them directly translate to codegen attrs. +fn process_builtin_attrs( + tcx: TyCtxt<'_>, + did: LocalDefId, + attrs: &[Attribute], + codegen_fn_attrs: &mut CodegenFnAttrs, +) -> InterestingAttributeDiagnosticSpans { + let mut interesting_spans = InterestingAttributeDiagnosticSpans::default(); + let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); // If our rustc version supports autodiff/enzyme, then we call our handler // to check for any `#[rustc_autodiff(...)]` attributes. + // FIXME(jdonszelmann): merge with loop below if cfg!(llvm_enzyme) { let ad = autodiff_attrs(tcx, did.into()); codegen_fn_attrs.autodiff_item = ad; } - // When `no_builtins` is applied at the crate level, we should add the - // `no-builtins` attribute to each function to ensure it takes effect in LTO. - let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); - let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); - if no_builtins { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; - } - - let rust_target_features = tcx.rust_target_features(LOCAL_CRATE); - - let mut link_ordinal_span = None; - let mut no_sanitize_span = None; - for attr in attrs.iter() { - // In some cases, attribute are only valid on functions, but it's the `check_attr` - // pass that check that they aren't used anywhere else, rather this module. - // In these cases, we bail from performing further checks that are only meaningful for - // functions (such as calling `fn_sig`, which ICEs if given a non-function). We also - // report a delayed bug, just in case `check_attr` isn't doing its job. - let fn_sig = |attr_span| { - use DefKind::*; - - let def_kind = tcx.def_kind(did); - if let Fn | AssocFn | Variant | Ctor(..) = def_kind { - Some(tcx.fn_sig(did)) - } else { - tcx.dcx() - .span_delayed_bug(attr_span, "this attribute can only be applied to functions"); - None - } - }; - if let hir::Attribute::Parsed(p) = attr { match p { AttributeKind::Cold(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD, AttributeKind::ExportName { name, .. } => { - codegen_fn_attrs.export_name = Some(*name); + codegen_fn_attrs.export_name = Some(*name) + } + AttributeKind::Inline(inline, span) => { + codegen_fn_attrs.inline = *inline; + interesting_spans.inline = Some(*span); } AttributeKind::Naked(_) => codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED, AttributeKind::Align { align, .. } => codegen_fn_attrs.alignment = Some(*align), AttributeKind::LinkName { name, .. } => codegen_fn_attrs.link_name = Some(*name), AttributeKind::LinkOrdinal { ordinal, span } => { codegen_fn_attrs.link_ordinal = Some(*ordinal); - link_ordinal_span = Some(*span); + interesting_spans.link_ordinal = Some(*span); } AttributeKind::LinkSection { name, .. } => { codegen_fn_attrs.link_section = Some(*name) } AttributeKind::NoMangle(attr_span) => { + interesting_spans.no_mangle = Some(*attr_span); if tcx.opt_item_name(did.to_def_id()).is_some() { codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else { @@ -137,6 +256,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }); } } + AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize, AttributeKind::TargetFeature(features, attr_span) => { let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else { tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn"); @@ -184,7 +304,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { let is_closure = tcx.is_closure_like(did.to_def_id()); if !is_closure - && let Some(fn_sig) = fn_sig(*attr_span) + && let Some(fn_sig) = try_fn_sig(tcx, did, *attr_span) && fn_sig.skip_binder().abi() != ExternAbi::Rust { tcx.dcx().emit_err(errors::RequiresRustAbi { span: *attr_span }); @@ -232,155 +352,49 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::linkage => { - if let Some(val) = attr.value_str() { - let linkage = Some(linkage_by_name(tcx, did, val.as_str())); - if tcx.is_foreign_item(did) { - codegen_fn_attrs.import_linkage = linkage; + let linkage = parse_linkage_attr(tcx, did, attr); - if tcx.is_mutable_static(did.into()) { - let mut diag = tcx.dcx().struct_span_err( - attr.span(), - "extern mutable statics are not allowed with `#[linkage]`", - ); - diag.note( - "marking the extern static mutable would allow changing which \ - symbol the static references rather than make the target of the \ - symbol mutable", - ); - diag.emit(); - } - } else { - codegen_fn_attrs.linkage = linkage; + if tcx.is_foreign_item(did) { + codegen_fn_attrs.import_linkage = linkage; + + if tcx.is_mutable_static(did.into()) { + let mut diag = tcx.dcx().struct_span_err( + attr.span(), + "extern mutable statics are not allowed with `#[linkage]`", + ); + diag.note( + "marking the extern static mutable would allow changing which \ + symbol the static references rather than make the target of the \ + symbol mutable", + ); + diag.emit(); } + } else { + codegen_fn_attrs.linkage = linkage; } } sym::no_sanitize => { - no_sanitize_span = Some(attr.span()); - if let Some(list) = attr.meta_item_list() { - for item in list.iter() { - match item.name() { - Some(sym::address) => { - codegen_fn_attrs.no_sanitize |= - SanitizerSet::ADDRESS | SanitizerSet::KERNELADDRESS - } - Some(sym::cfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI, - Some(sym::kcfi) => codegen_fn_attrs.no_sanitize |= SanitizerSet::KCFI, - Some(sym::memory) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY - } - Some(sym::memtag) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG - } - Some(sym::shadow_call_stack) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK - } - Some(sym::thread) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD - } - Some(sym::hwaddress) => { - codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS - } - _ => { - tcx.dcx().emit_err(errors::InvalidNoSanitize { span: item.span() }); - } - } - } - } + interesting_spans.no_sanitize = Some(attr.span()); + codegen_fn_attrs.no_sanitize |= + parse_no_sanitize_attr(tcx, attr).unwrap_or_default(); } sym::instruction_set => { - codegen_fn_attrs.instruction_set = - attr.meta_item_list().and_then(|l| match &l[..] { - [MetaItemInner::MetaItem(set)] => { - let segments = - set.path.segments.iter().map(|x| x.ident.name).collect::>(); - match segments.as_slice() { - [sym::arm, sym::a32 | sym::t32] - if !tcx.sess.target.has_thumb_interworking => - { - tcx.dcx().emit_err(errors::UnsupportedInstructionSet { - span: attr.span(), - }); - None - } - [sym::arm, sym::a32] => Some(InstructionSetAttr::ArmA32), - [sym::arm, sym::t32] => Some(InstructionSetAttr::ArmT32), - _ => { - tcx.dcx().emit_err(errors::InvalidInstructionSet { - span: attr.span(), - }); - None - } - } - } - [] => { - tcx.dcx().emit_err(errors::BareInstructionSet { span: attr.span() }); - None - } - _ => { - tcx.dcx() - .emit_err(errors::MultipleInstructionSet { span: attr.span() }); - None - } - }) + codegen_fn_attrs.instruction_set = parse_instruction_set_attr(tcx, attr) } sym::patchable_function_entry => { - codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| { - let mut prefix = None; - let mut entry = None; - for item in l { - let Some(meta_item) = item.meta_item() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let Some(name_value_lit) = meta_item.name_value_literal() else { - tcx.dcx().emit_err(errors::ExpectedNameValuePair { span: item.span() }); - continue; - }; - - let attrib_to_write = match meta_item.name() { - Some(sym::prefix_nops) => &mut prefix, - Some(sym::entry_nops) => &mut entry, - _ => { - tcx.dcx().emit_err(errors::UnexpectedParameterName { - span: item.span(), - prefix_nops: sym::prefix_nops, - entry_nops: sym::entry_nops, - }); - continue; - } - }; - - let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else { - tcx.dcx().emit_err(errors::InvalidLiteralValue { - span: name_value_lit.span, - }); - continue; - }; - - let Ok(val) = val.get().try_into() else { - tcx.dcx() - .emit_err(errors::OutOfRangeInteger { span: name_value_lit.span }); - continue; - }; - - *attrib_to_write = Some(val); - } - - if let (None, None) = (prefix, entry) { - tcx.dcx().span_err(attr.span(), "must specify at least one parameter"); - } - - Some(PatchableFunctionEntry::from_prefix_and_entry( - prefix.unwrap_or(0), - entry.unwrap_or(0), - )) - }) + codegen_fn_attrs.patchable_function_entry = + parse_patchable_function_entry(tcx, attr); } _ => {} } } + interesting_spans +} + +/// Applies overrides for codegen fn attrs. These often have a specific reason why they're necessary. +/// Please comment why when adding a new one! +fn apply_overrides(tcx: TyCtxt<'_>, did: LocalDefId, codegen_fn_attrs: &mut CodegenFnAttrs) { // Apply the minimum function alignment here. This ensures that a function's alignment is // determined by the `-C` flags of the crate it is defined in, not the `-C` flags of the crate // it happens to be codegen'd (or const-eval'd) in. @@ -390,15 +404,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // On trait methods, inherit the `#[align]` of the trait's method prototype. codegen_fn_attrs.alignment = Ord::max(codegen_fn_attrs.alignment, tcx.inherited_align(did)); - let inline_span; - (codegen_fn_attrs.inline, inline_span) = if let Some((inline_attr, span)) = - find_attr!(attrs, AttributeKind::Inline(i, span) => (*i, *span)) - { - (inline_attr, Some(span)) - } else { - (InlineAttr::None, None) - }; - // naked function MUST NOT be inlined! This attribute is required for the rust compiler itself, // but not for the code generation backend because at that point the naked function will just be // a declaration, with a definition provided in global assembly. @@ -406,9 +411,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { codegen_fn_attrs.inline = InlineAttr::Never; } - codegen_fn_attrs.optimize = - find_attr!(attrs, AttributeKind::Optimize(i, _) => *i).unwrap_or(OptimizeAttr::Default); - // #73631: closures inherit `#[target_feature]` annotations // // If this closure is marked `#[inline(always)]`, simply skip adding `#[target_feature]`. @@ -431,6 +433,26 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } } + // When `no_builtins` is applied at the crate level, we should add the + // `no-builtins` attribute to each function to ensure it takes effect in LTO. + let crate_attrs = tcx.hir_attrs(rustc_hir::CRATE_HIR_ID); + let no_builtins = attr::contains_name(crate_attrs, sym::no_builtins); + if no_builtins { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS; + } + + // inherit track-caller properly + if tcx.should_inherit_track_caller(did) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER; + } +} + +fn check_result( + tcx: TyCtxt<'_>, + did: LocalDefId, + interesting_spans: InterestingAttributeDiagnosticSpans, + codegen_fn_attrs: &CodegenFnAttrs, +) { // If a function uses `#[target_feature]` it can't be inlined into general // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid `#[inline(always)]` as it can't be @@ -446,14 +468,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) - && let Some(span) = inline_span + && let Some(span) = interesting_spans.inline { tcx.dcx().span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); } + // warn that inline has no effect when no_sanitize is present if !codegen_fn_attrs.no_sanitize.is_empty() && codegen_fn_attrs.inline.always() - && let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) + && let (Some(no_sanitize_span), Some(inline_span)) = + (interesting_spans.no_sanitize, interesting_spans.inline) { let hir_id = tcx.local_def_id_to_hir_id(did); tcx.node_span_lint(lint::builtin::INLINE_NO_SANITIZE, hir_id, no_sanitize_span, |lint| { @@ -462,51 +486,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }) } - // Weak lang items have the same semantics as "std internal" symbols in the - // sense that they're preserved through all our LTO passes and only - // strippable by the linker. - // - // Additionally weak lang items have predetermined symbol names. - if let Some((name, _)) = lang_items::extract(attrs) - && let Some(lang_item) = LangItem::from_name(name) + // error when specifying link_name together with link_ordinal + if let Some(_) = codegen_fn_attrs.link_name + && let Some(_) = codegen_fn_attrs.link_ordinal { - if WEAK_LANG_ITEMS.contains(&lang_item) { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; + if let Some(span) = interesting_spans.link_ordinal { + tcx.dcx().span_err(span, msg); + } else { + tcx.dcx().err(msg); } - if let Some(link_name) = lang_item.link_name() { - codegen_fn_attrs.export_name = Some(link_name); - codegen_fn_attrs.link_name = Some(link_name); - } - } - check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span); - - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) - && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) - { - let no_mangle_span = - find_attr!(attrs, AttributeKind::NoMangle(no_mangle_span) => *no_mangle_span) - .unwrap_or_default(); - let lang_item = - lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name)); - let mut err = tcx - .dcx() - .struct_span_err( - no_mangle_span, - "`#[no_mangle]` cannot be used on internal language items", - ) - .with_note("Rustc requires this item to have a specific mangled name.") - .with_span_label(tcx.def_span(did), "should be the internal language item"); - if let Some(lang_item) = lang_item - && let Some(link_name) = lang_item.link_name() - { - err = err - .with_note("If you are trying to prevent mangling to ease debugging, many") - .with_note(format!("debuggers support a command such as `rbreak {link_name}` to")) - .with_note(format!( - "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" - )) - } - err.emit(); } if let Some(features) = check_tied_features( @@ -529,6 +518,84 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { }) .emit(); } +} + +fn handle_lang_items( + tcx: TyCtxt<'_>, + did: LocalDefId, + interesting_spans: &InterestingAttributeDiagnosticSpans, + attrs: &[Attribute], + codegen_fn_attrs: &mut CodegenFnAttrs, +) { + // Weak lang items have the same semantics as "std internal" symbols in the + // sense that they're preserved through all our LTO passes and only + // strippable by the linker. + // + // Additionally weak lang items have predetermined symbol names. + if let Some((name, _)) = lang_items::extract(attrs) + && let Some(lang_item) = LangItem::from_name(name) + { + if WEAK_LANG_ITEMS.contains(&lang_item) { + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + } + if let Some(link_name) = lang_item.link_name() { + codegen_fn_attrs.export_name = Some(link_name); + codegen_fn_attrs.link_name = Some(link_name); + } + } + + // error when using no_mangle on a lang item item + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + && codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) + { + let lang_item = + lang_items::extract(attrs).map_or(None, |(name, _span)| LangItem::from_name(name)); + let mut err = tcx + .dcx() + .struct_span_err( + interesting_spans.no_mangle.unwrap_or_default(), + "`#[no_mangle]` cannot be used on internal language items", + ) + .with_note("Rustc requires this item to have a specific mangled name.") + .with_span_label(tcx.def_span(did), "should be the internal language item"); + if let Some(lang_item) = lang_item + && let Some(link_name) = lang_item.link_name() + { + err = err + .with_note("If you are trying to prevent mangling to ease debugging, many") + .with_note(format!("debuggers support a command such as `rbreak {link_name}` to")) + .with_note(format!( + "match `.*{link_name}.*` instead of `break {link_name}` on a specific name" + )) + } + err.emit(); + } +} + +/// Generate the [`CodegenFnAttrs`] for an item (identified by the [`LocalDefId`]). +/// +/// This happens in 4 stages: +/// - apply built-in attributes that directly translate to codegen attributes. +/// - handle lang items. These have special codegen attrs applied to them. +/// - apply overrides, like minimum requirements for alignment and other settings that don't rely directly the built-in attrs on the item. +/// overrides come after applying built-in attributes since they may only apply when certain attributes were already set in the stage before. +/// - check that the result is valid. There's various ways in which this may not be the case, such as certain combinations of attrs. +fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { + if cfg!(debug_assertions) { + let def_kind = tcx.def_kind(did); + assert!( + def_kind.has_codegen_attrs(), + "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}", + ); + } + + let mut codegen_fn_attrs = CodegenFnAttrs::new(); + let attrs = tcx.hir_attrs(tcx.local_def_id_to_hir_id(did)); + + let interesting_spans = process_builtin_attrs(tcx, did, attrs, &mut codegen_fn_attrs); + handle_lang_items(tcx, did, &interesting_spans, attrs, &mut codegen_fn_attrs); + apply_overrides(tcx, did, &mut codegen_fn_attrs); + check_result(tcx, did, interesting_spans, &codegen_fn_attrs); codegen_fn_attrs } @@ -555,27 +622,12 @@ fn inherited_align<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option { tcx.codegen_fn_attrs(opt_trait_item(tcx, def_id)?).alignment } -fn check_link_name_xor_ordinal( - tcx: TyCtxt<'_>, - codegen_fn_attrs: &CodegenFnAttrs, - inline_span: Option, -) { - if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() { - return; - } - let msg = "cannot use `#[link_name]` with `#[link_ordinal]`"; - if let Some(span) = inline_span { - tcx.dcx().span_err(span, msg); - } else { - tcx.dcx().err(msg); - } -} - /// We now check the #\[rustc_autodiff\] attributes which we generated from the #[autodiff(...)] /// macros. There are two forms. The pure one without args to mark primal functions (the functions /// being differentiated). The other form is #[rustc_autodiff(Mode, ActivityList)] on top of the /// placeholder functions. We wrote the rustc_autodiff attributes ourself, so this should never /// panic, unless we introduced a bug when parsing the autodiff macro. +//FIXME(jdonszelmann): put in the main loop. No need to have two..... :/ Let's do that when we make autodiff parsed. fn autodiff_attrs(tcx: TyCtxt<'_>, id: DefId) -> Option { let attrs = tcx.get_attrs(id, sym::rustc_autodiff);