diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 3c6dbb466db7a..396b2cefa81c3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -192,6 +192,12 @@ lint_redundant_semicolons = *[false] this semicolon } +lint_unnecessary_send_constraint = constraining a reference to `Send` is meaningless + .suggestion = {$only_trait -> + [true] replace this with `std::any::Any` + *[false] remove this + } + lint_drop_trait_constraints = bounds on `{$predicate}` are most likely incorrect, consider instead using `{$needs_drop}` to detect whether a type can be trivially dropped diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 319eb2ea445ed..a555d8b8aafa4 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -76,6 +76,7 @@ mod passes; mod redundant_semicolon; mod traits; mod types; +mod unnecessary_send_constraint; mod unused; pub use array_into_iter::ARRAY_INTO_ITER; @@ -125,6 +126,7 @@ pub use passes::{EarlyLintPass, LateLintPass}; pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; pub use rustc_session::lint::{LintArray, LintPass}; +use unnecessary_send_constraint::UnnecessarySendConstraint; fluent_messages! { "../messages.ftl" } @@ -242,6 +244,7 @@ late_lint_methods!( OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, MultipleSupertraitUpcastable: MultipleSupertraitUpcastable, MapUnitFn: MapUnitFn, + UnnecessarySendConstraint: UnnecessarySendConstraint, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 848f6a9ecb532..7e18d2dd0e938 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1168,6 +1168,15 @@ pub struct RedundantSemicolonsDiag { pub suggestion: Span, } +// lint_unnecessary_send_constraint.rs +#[derive(LintDiagnostic)] +#[diag(lint_unnecessary_send_constraint)] +pub struct UnnecessarySendConstraintDiag { + pub only_trait: bool, + #[suggestion(code = "", applicability = "maybe-incorrect")] + pub suggestion: Span, +} + // traits.rs pub struct DropTraitConstraintsDiag<'a> { pub predicate: Predicate<'a>, diff --git a/compiler/rustc_lint/src/unnecessary_send_constraint.rs b/compiler/rustc_lint/src/unnecessary_send_constraint.rs new file mode 100644 index 0000000000000..40395869843a4 --- /dev/null +++ b/compiler/rustc_lint/src/unnecessary_send_constraint.rs @@ -0,0 +1,57 @@ +use rustc_span::sym; + +use crate::hir; + +use crate::{lints::UnnecessarySendConstraintDiag, LateContext, LateLintPass}; + +declare_lint! { + /// The `unnecessary_send_constraint` lints unnecessary constraint of references to `Send`. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #[deny(unnecessary_send_constraint)] + /// fn foo(_: &(dyn Any + Send)) {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Shared references cannot be sent across threads unless they have a `Sync` bound, so constraining them to `Send` without `Sync` is unnecessary. + pub UNNECESSARY_SEND_CONSTRAINT, + Warn, + "constraining a reference to `Send` without `Sync` is unnecessary, consider removing it" +} + +declare_lint_pass!(UnnecessarySendConstraint => [UNNECESSARY_SEND_CONSTRAINT]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessarySendConstraint { + fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'tcx>) { + let hir::TyKind::Ref( + .., + hir::MutTy { + ty: hir::Ty { + kind: hir::TyKind::TraitObject(bounds, ..), + .. + }, + .. + }, + ) = ty.kind else { return; }; + + let send = cx.tcx.get_diagnostic_item(sym::Send); + + let send_bound = bounds.iter().find(|b| b.trait_ref.trait_def_id() == send); + + if let Some(send_bound) = send_bound { + let only_trait = bounds.len() == 1; + + cx.tcx.emit_spanned_lint( + UNNECESSARY_SEND_CONSTRAINT, + send_bound.trait_ref.hir_ref_id, // is this correct? + send_bound.span, + UnnecessarySendConstraintDiag { only_trait, suggestion: send_bound.span }, + ) + } + } +} diff --git a/library/core/src/any.rs b/library/core/src/any.rs index bb93ea509d8ee..3f7402d306cf4 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -398,7 +398,7 @@ impl dyn Any + Send { /// ``` /// use std::any::Any; /// - /// fn is_string(s: &(dyn Any + Send)) { + /// fn is_string(s: &dyn Any) { /// if s.is::() { /// println!("It's a string!"); /// } else { @@ -422,7 +422,7 @@ impl dyn Any + Send { /// ``` /// use std::any::Any; /// - /// fn print_if_string(s: &(dyn Any + Send)) { + /// fn print_if_string(s: &dyn Any) { /// if let Some(string) = s.downcast_ref::() { /// println!("It's a string({}): '{}'", string.len(), string); /// } else { diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index bcca8d924cdd6..8031052b90061 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2246,6 +2246,7 @@ impl, U> CoerceUnsized> for SyncUnsafeCell impl, U> DispatchFromDyn> for SyncUnsafeCell {} #[allow(unused)] +#[cfg_attr(not(bootstrap), allow(unnecessary_send_constraint))] fn assert_coerce_unsized( a: UnsafeCell<&i32>, b: SyncUnsafeCell<&i32>, diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs index 8338a5d7e5a21..1cf9b5e937181 100644 --- a/library/core/src/panic.rs +++ b/library/core/src/panic.rs @@ -107,5 +107,5 @@ pub unsafe trait BoxMeUp { fn take_box(&mut self) -> *mut (dyn Any + Send); /// Just borrow the contents. - fn get(&mut self) -> &(dyn Any + Send); + fn get(&mut self) -> &dyn Any; } diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index 06fbe083ca13d..f95ca251118aa 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -24,7 +24,7 @@ use crate::panic::Location; #[stable(feature = "panic_hooks", since = "1.10.0")] #[derive(Debug)] pub struct PanicInfo<'a> { - payload: &'a (dyn Any + Send), + payload: &'a dyn Any, message: Option<&'a fmt::Arguments<'a>>, location: &'a Location<'a>, can_unwind: bool, @@ -54,7 +54,7 @@ impl<'a> PanicInfo<'a> { )] #[doc(hidden)] #[inline] - pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) { + pub fn set_payload(&mut self, info: &'a dyn Any) { self.payload = info; } @@ -81,7 +81,7 @@ impl<'a> PanicInfo<'a> { /// ``` #[must_use] #[stable(feature = "panic_hooks", since = "1.10.0")] - pub fn payload(&self) -> &(dyn Any + Send) { + pub fn payload(&self) -> &dyn Any { self.payload } diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index efeb726ab8eb8..3ca81b70a69aa 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -11,7 +11,7 @@ //! ``` //! //! This definition allows for panicking with any general message, but it does not -//! allow for failing with a `Box` value. (`PanicInfo` just contains a `&(dyn Any + Send)`, +//! allow for failing with a `Box` value. (`PanicInfo` just contains a `&dyn Any`, //! for which we fill in a dummy value in `PanicInfo::internal_constructor`.) //! The reason for this is that core is not allowed to allocate. //! diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 34c0ce9dcf848..107a48bcb655f 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -695,7 +695,7 @@ impl Error { #[stable(feature = "io_error_inner", since = "1.3.0")] #[must_use] #[inline] - pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> { + pub fn get_ref(&self) -> Option<&(dyn error::Error + Sync + 'static)> { match self.repr.data() { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, @@ -769,7 +769,7 @@ impl Error { #[stable(feature = "io_error_inner", since = "1.3.0")] #[must_use] #[inline] - pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> { + pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Sync + 'static)> { match self.repr.data_mut() { ErrorData::Os(..) => None, ErrorData::Simple(..) => None, diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index a46a29cbad608..c3a4fbf2154c5 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -220,10 +220,7 @@ pub fn take_hook() -> Box) + 'static + Sync + Send> { #[unstable(feature = "panic_update_hook", issue = "92649")] pub fn update_hook(hook_fn: F) where - F: Fn(&(dyn Fn(&PanicInfo<'_>) + Send + Sync + 'static), &PanicInfo<'_>) - + Sync - + Send - + 'static, + F: Fn(&(dyn Fn(&PanicInfo<'_>) + Sync + 'static), &PanicInfo<'_>) + Sync + Send + 'static, { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); @@ -556,7 +553,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { Box::into_raw(Box::new(contents)) } - fn get(&mut self) -> &(dyn Any + Send) { + fn get(&mut self) -> &dyn Any { self.fill() } } @@ -568,7 +565,7 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { Box::into_raw(Box::new(self.0)) } - fn get(&mut self) -> &(dyn Any + Send) { + fn get(&mut self) -> &dyn Any { &self.0 } } @@ -635,7 +632,7 @@ pub const fn begin_panic(msg: M) -> ! { Box::into_raw(data) } - fn get(&mut self) -> &(dyn Any + Send) { + fn get(&mut self) -> &dyn Any { match self.inner { Some(ref a) => a, None => process::abort(), @@ -725,7 +722,7 @@ pub fn rust_panic_without_hook(payload: Box) -> ! { Box::into_raw(mem::replace(&mut self.0, Box::new(()))) } - fn get(&mut self) -> &(dyn Any + Send) { + fn get(&mut self) -> &dyn Any { &*self.0 } } diff --git a/library/test/src/test_result.rs b/library/test/src/test_result.rs index 1da238e3e8c0f..c0cfd196d8845 100644 --- a/library/test/src/test_result.rs +++ b/library/test/src/test_result.rs @@ -27,7 +27,7 @@ pub enum TestResult { /// and associated data. pub fn calc_result<'a>( desc: &TestDesc, - task_result: Result<(), &'a (dyn Any + 'static + Send)>, + task_result: Result<(), &'a (dyn Any + 'static)>, time_opts: &Option, exec_time: &Option, ) -> TestResult { diff --git a/tests/ui/lint/unnecessary-send-constraint/unnecessary-send-constraint.rs b/tests/ui/lint/unnecessary-send-constraint/unnecessary-send-constraint.rs new file mode 100644 index 0000000000000..f6484a41c1f04 --- /dev/null +++ b/tests/ui/lint/unnecessary-send-constraint/unnecessary-send-constraint.rs @@ -0,0 +1,13 @@ +#![warn(unnecessary_send_constraint)] + +use std::any::Any; + +fn main() {} + +fn fine(_a: &dyn Any) {} + +fn should_replace_with_any(_a: &(dyn Send)) {} + +fn should_remove_send(_a: &(dyn Any + Send)) {} + +fn should_remove_send_duplicate(_a: &(dyn Any + Send)) {}