diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index 498000db50f43..7b788b13b9f7d 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -343,4 +343,7 @@ language_item_table! { Range, sym::Range, range_struct, Target::Struct; RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct; RangeTo, sym::RangeTo, range_to_struct, Target::Struct; + Send, sym::send, send_trait, Target::Trait; + UnwindSafe, sym::unwind_safe, unwind_safe_trait, Target::Trait; + RefUnwindSafe, sym::ref_unwind_safe, ref_unwind_safe_trait, Target::Trait; } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index beb4d36597cfc..15246971bae02 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2995,7 +2995,7 @@ declare_lint_pass! { UNSUPPORTED_NAKED_FUNCTIONS, MISSING_ABI, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS, - DISJOINT_CAPTURE_DROP_REORDER, + DISJOINT_CAPTURE_MIGRATION, LEGACY_DERIVE_HELPERS, PROC_MACRO_BACK_COMPAT, OR_PATTERNS_BACK_COMPAT, @@ -3027,14 +3027,17 @@ declare_lint! { } declare_lint! { - /// The `disjoint_capture_drop_reorder` lint detects variables that aren't completely + /// The `disjoint_capture_migration` lint detects variables that aren't completely /// captured when the feature `capture_disjoint_fields` is enabled and it affects the Drop /// order of at least one path starting at this variable. + /// It can also detect when a variable implements a trait, but one of its field does not and + /// the field is captured by a closure and used with the assumption that said field implements + /// the same trait as the root variable. /// - /// ### Example + /// ### Example of drop reorder /// /// ```rust,compile_fail - /// # #![deny(disjoint_capture_drop_reorder)] + /// # #![deny(disjoint_capture_migration)] /// # #![allow(unused)] /// struct FancyInteger(i32); /// @@ -3065,10 +3068,35 @@ declare_lint! { /// /// In the above example `p.y` will be dropped at the end of `f` instead of with `c` if /// the feature `capture_disjoint_fields` is enabled. - pub DISJOINT_CAPTURE_DROP_REORDER, + /// + /// ### Example of auto-trait + /// + /// ```rust,compile_fail + /// #![deny(disjoint_capture_migration)] + /// use std::thread; + /// + /// struct Pointer (*mut i32); + /// unsafe impl Send for Pointer {} + /// + /// fn main() { + /// let mut f = 10; + /// let fptr = Pointer(&mut f as *mut i32); + /// thread::spawn(move || unsafe { + /// *fptr.0 = 20; + /// }); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In the above example `fptr.0` is captured when feature `capture_disjoint_fields` is enabled. + /// The field is of type *mut i32 which doesn't implement Send, making the code invalid as the + /// field cannot be sent between thread safely. + pub DISJOINT_CAPTURE_MIGRATION, Allow, - "Drop reorder because of `capture_disjoint_fields`" - + "Drop reorder and auto traits error because of `capture_disjoint_fields`" } declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b2dac10c83fac..6ba68fb767d61 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -933,6 +933,7 @@ symbols! { receiver, recursion_limit, reexport_test_harness_main, + ref_unwind_safe, reference, reflect, reg, @@ -1054,6 +1055,7 @@ symbols! { self_in_typedefs, self_struct_ctor, semitransparent, + send, send_trait, shl, shl_assign, @@ -1275,6 +1277,7 @@ symbols! { unused_qualifications, unwind, unwind_attributes, + unwind_safe, unwrap, unwrap_or, use_extern_macros, diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index 751eebb9f9564..7a2b5b26ef437 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -42,11 +42,13 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_infer::infer::UpvarRegion; use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults, UpvarSubsts}; +use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt, TypeckResults, UpvarSubsts}; use rustc_session::lint; use rustc_span::sym; use rustc_span::{MultiSpan, Span, Symbol}; +use rustc_trait_selection::traits::{Obligation, ObligationCause}; +use rustc_data_structures::stable_set::FxHashSet; use rustc_index::vec::Idx; use rustc_target::abi::VariantIdx; @@ -168,7 +170,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.compute_min_captures(closure_def_id, delegate.capture_information); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); - if should_do_migration_analysis(self.tcx, closure_hir_id) { + + if should_do_disjoint_capture_migration_analysis(self.tcx, closure_hir_id) { self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span); } @@ -471,7 +474,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { capture_clause: hir::CaptureBy, span: Span, ) { - let need_migrations = self.compute_2229_migrations( + let (need_migrations, reasons) = self.compute_2229_migrations( closure_def_id, span, capture_clause, @@ -485,12 +488,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let local_def_id = closure_def_id.expect_local(); let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(local_def_id); self.tcx.struct_span_lint_hir( - lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, + lint::builtin::DISJOINT_CAPTURE_MIGRATION, closure_hir_id, span, |lint| { let mut diagnostics_builder = lint.build( - "drop order affected for closure because of `capture_disjoint_fields`", + format!( + "{} affected for closure because of `capture_disjoint_fields`", + reasons + ) + .as_str(), ); let closure_body_span = self.tcx.hir().span(body_id.hir_id); let (sugg, app) = @@ -527,6 +534,169 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Combines all the reasons for 2229 migrations + fn compute_2229_migrations_reasons( + &self, + auto_trait_reasons: FxHashSet<&str>, + drop_reason: bool, + ) -> String { + let mut reasons = String::new(); + + if auto_trait_reasons.len() > 0 { + reasons = format!( + "{} trait implementation", + auto_trait_reasons.clone().into_iter().collect::>().join(", ") + ); + } + + if auto_trait_reasons.len() > 0 && drop_reason { + reasons = format!("{}, and ", reasons); + } + + if drop_reason { + reasons = format!("{}drop order", reasons); + } + + reasons + } + + /// Returns true if `ty` may implement `trait_def_id` + fn ty_impls_trait( + &self, + ty: Ty<'tcx>, + cause: &ObligationCause<'tcx>, + trait_def_id: DefId, + ) -> bool { + use crate::rustc_middle::ty::ToPredicate; + use crate::rustc_middle::ty::WithConstness; + use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; + let tcx = self.infcx.tcx; + + let trait_ref = TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, &[]) }; + + let obligation = Obligation::new( + cause.clone(), + self.param_env, + trait_ref.without_const().to_predicate(tcx), + ); + + self.infcx.predicate_may_hold(&obligation) + } + + /// Returns true if migration is needed for trait for the provided var_hir_id + fn need_2229_migrations_for_trait( + &self, + min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, + var_hir_id: hir::HirId, + check_trait: Option, + ) -> bool { + let root_var_min_capture_list = if let Some(root_var_min_capture_list) = + min_captures.and_then(|m| m.get(&var_hir_id)) + { + root_var_min_capture_list + } else { + return false; + }; + + let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + + let cause = ObligationCause::misc(self.tcx.hir().span(var_hir_id), self.body_id); + + let obligation_should_hold = check_trait + .map(|check_trait| self.ty_impls_trait(ty, &cause, check_trait)) + .unwrap_or(false); + + // Check whether catpured fields also implement the trait + + for capture in root_var_min_capture_list.iter() { + let ty = capture.place.ty(); + + let obligation_holds_for_capture = check_trait + .map(|check_trait| self.ty_impls_trait(ty, &cause, check_trait)) + .unwrap_or(false); + + if !obligation_holds_for_capture && obligation_should_hold { + return true; + } + } + false + } + + /// Figures out the list of root variables (and their types) that aren't completely + /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits + /// differ between the root variable and the captured paths. + /// + /// The output list would include a root variable if: + /// - It would have been captured into the closure when `capture_disjoint_fields` wasn't + /// enabled, **and** + /// - It wasn't completely captured by the closure, **and** + /// - One of the paths captured does not implement all the auto-traits its root variable + /// implements. + fn compute_2229_migrations_for_trait( + &self, + min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, + var_hir_id: hir::HirId, + ) -> Option> { + let tcx = self.infcx.tcx; + + // Check whether catpured fields also implement the trait + let mut auto_trait_reasons = FxHashSet::default(); + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().clone_trait(), + ) { + auto_trait_reasons.insert("`Clone`"); + } + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().sync_trait(), + ) { + auto_trait_reasons.insert("`Sync`"); + } + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().send_trait(), + ) { + auto_trait_reasons.insert("`Send`"); + } + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().unpin_trait(), + ) { + auto_trait_reasons.insert("`Unpin`"); + } + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().unwind_safe_trait(), + ) { + auto_trait_reasons.insert("`UnwindSafe`"); + } + + if self.need_2229_migrations_for_trait( + min_captures, + var_hir_id, + tcx.lang_items().ref_unwind_safe_trait(), + ) { + auto_trait_reasons.insert("`RefUnwindSafe`"); + } + + if auto_trait_reasons.len() > 0 { + return Some(auto_trait_reasons); + } + + return None; + } + /// Figures out the list of root variables (and their types) that aren't completely /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of /// some path starting at that root variable **might** be affected. @@ -536,76 +706,128 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// enabled, **and** /// - It wasn't completely captured by the closure, **and** /// - One of the paths starting at this root variable, that is not captured needs Drop. - fn compute_2229_migrations( + fn compute_2229_migrations_for_drop( &self, closure_def_id: DefId, closure_span: Span, - closure_clause: hir::CaptureBy, min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - ) -> Vec { - let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - upvars - } else { - return vec![]; - }; + closure_clause: hir::CaptureBy, + var_hir_id: hir::HirId, + ) -> bool { + let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); - let mut need_migrations = Vec::new(); + if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { + return false; + } - for (&var_hir_id, _) in upvars.iter() { - let ty = self.infcx.resolve_vars_if_possible(self.node_ty(var_hir_id)); + let root_var_min_capture_list = if let Some(root_var_min_capture_list) = + min_captures.and_then(|m| m.get(&var_hir_id)) + { + root_var_min_capture_list + } else { + // The upvar is mentioned within the closure but no path starting from it is + // used. - if !ty.needs_drop(self.tcx, self.tcx.param_env(closure_def_id.expect_local())) { - continue; + match closure_clause { + // Only migrate if closure is a move closure + hir::CaptureBy::Value => return true, + hir::CaptureBy::Ref => {} } - let root_var_min_capture_list = if let Some(root_var_min_capture_list) = - min_captures.and_then(|m| m.get(&var_hir_id)) - { - root_var_min_capture_list - } else { - // The upvar is mentioned within the closure but no path starting from it is - // used. + return false; + }; - match closure_clause { - // Only migrate if closure is a move closure - hir::CaptureBy::Value => need_migrations.push(var_hir_id), + let projections_list = root_var_min_capture_list + .iter() + .filter_map(|captured_place| match captured_place.info.capture_kind { + // Only care about captures that are moved into the closure + ty::UpvarCapture::ByValue(..) => Some(captured_place.place.projections.as_slice()), + ty::UpvarCapture::ByRef(..) => None, + }) + .collect::>(); - hir::CaptureBy::Ref => {} - } + let is_moved = !projections_list.is_empty(); - continue; - }; + let is_not_completely_captured = + root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0); - let projections_list = root_var_min_capture_list - .iter() - .filter_map(|captured_place| match captured_place.info.capture_kind { - // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue(..) => { - Some(captured_place.place.projections.as_slice()) - } - ty::UpvarCapture::ByRef(..) => None, - }) - .collect::>(); + if is_moved + && is_not_completely_captured + && self.has_significant_drop_outside_of_captures( + closure_def_id, + closure_span, + ty, + projections_list, + ) + { + return true; + } + + return false; + } - let is_moved = !projections_list.is_empty(); + /// Figures out the list of root variables (and their types) that aren't completely + /// captured by the closure when `capture_disjoint_fields` is enabled and either drop + /// order of some path starting at that root variable **might** be affected or auto-traits + /// differ between the root variable and the captured paths. + /// + /// The output list would include a root variable if: + /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't + /// enabled, **and** + /// - It wasn't completely captured by the closure, **and** + /// - One of the paths starting at this root variable, that is not captured needs Drop **or** + /// - One of the paths captured does not implement all the auto-traits its root variable + /// implements. + /// + /// Returns a tuple containing a vector of HirIds as well as a String containing the reason + /// why root variables whose HirId is contained in the vector should be fully captured. + fn compute_2229_migrations( + &self, + closure_def_id: DefId, + closure_span: Span, + closure_clause: hir::CaptureBy, + min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, + ) -> (Vec, String) { + let upvars = if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { + upvars + } else { + return (Vec::new(), format!("")); + }; - let is_not_completely_captured = - root_var_min_capture_list.iter().any(|capture| capture.place.projections.len() > 0); + let mut need_migrations = Vec::new(); + let mut auto_trait_reasons = FxHashSet::default(); + let mut drop_reorder_reason = false; - if is_moved - && is_not_completely_captured - && self.has_significant_drop_outside_of_captures( - closure_def_id, - closure_span, - ty, - projections_list, - ) + // Perform auto-trait analysis + for (&var_hir_id, _) in upvars.iter() { + let mut need_migration = false; + if let Some(trait_migration_cause) = + self.compute_2229_migrations_for_trait(min_captures, var_hir_id) { + need_migration = true; + auto_trait_reasons.extend(trait_migration_cause); + } + + if self.compute_2229_migrations_for_drop( + closure_def_id, + closure_span, + min_captures, + closure_clause, + var_hir_id, + ) { + need_migration = true; + drop_reorder_reason = true; + } + + if need_migration { need_migrations.push(var_hir_id); } } - need_migrations + ( + need_migrations, + self.compute_2229_migrations_reasons(auto_trait_reasons, drop_reorder_reason), + ) } /// This is a helper function to `compute_2229_migrations_precise_pass`. Provided the type @@ -1544,9 +1766,8 @@ fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { tcx.hir().name(var_hir_id) } -fn should_do_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool { - let (level, _) = - tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_DROP_REORDER, closure_id); +fn should_do_disjoint_capture_migration_analysis(tcx: TyCtxt<'_>, closure_id: hir::HirId) -> bool { + let (level, _) = tcx.lint_level_at_node(lint::builtin::DISJOINT_CAPTURE_MIGRATION, closure_id); !matches!(level, lint::Level::Allow) } diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index fb957348bebd3..2b2404550433d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -31,6 +31,7 @@ use crate::hash::Hasher; /// [ub]: ../../reference/behavior-considered-undefined.html #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "send_trait")] +#[cfg_attr(not(bootstrap), lang = "send")] #[rustc_on_unimplemented( message = "`{Self}` cannot be sent between threads safely", label = "`{Self}` cannot be sent between threads safely" diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 3e634239ad301..7114552745a02 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -132,6 +132,7 @@ pub fn panic_any(msg: M) -> ! { /// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be /// implemented for any closed over variables passed to `catch_unwind`. #[stable(feature = "catch_unwind", since = "1.9.0")] +#[cfg_attr(all(not(bootstrap), not(test)), lang = "unwind_safe")] #[rustc_on_unimplemented( message = "the type `{Self}` may not be safely transferred across an unwind boundary", label = "`{Self}` may not be safely transferred across an unwind boundary" @@ -147,6 +148,7 @@ pub auto trait UnwindSafe {} /// This is a "helper marker trait" used to provide impl blocks for the /// [`UnwindSafe`] trait, for more information see that documentation. #[stable(feature = "catch_unwind", since = "1.9.0")] +#[cfg_attr(all(not(bootstrap), not(test)), lang = "ref_unwind_safe")] #[rustc_on_unimplemented( message = "the type `{Self}` may contain interior mutability and a reference may not be safely \ transferrable across a catch_unwind boundary", diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed new file mode 100644 index 0000000000000..93e6cf034055a --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.fixed @@ -0,0 +1,67 @@ +// run-rustfix +#![deny(disjoint_capture_migration)] + +use std::thread; + +/* Test Send Trait Migration */ +struct SendPointer (*mut i32); +unsafe impl Send for SendPointer {} + +fn test_send_trait() { + let mut f = 10; + let fptr = SendPointer(&mut f as *mut i32); + thread::spawn(move || { let _ = &fptr; unsafe { + //~^ ERROR: `Send` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0 = 20; + } }); +} + +/* Test Sync Trait Migration */ +struct CustomInt (*mut i32); +struct SyncPointer (CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_sync_trait() { + let mut f = 10; + let f = CustomInt(&mut f as *mut i32); + let fptr = SyncPointer(f); + thread::spawn(move || { let _ = &fptr; unsafe { + //~^ ERROR: `Sync`, `Send` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0.0 = 20; + } }); +} + +/* Test Clone Trait Migration */ +struct S(String); +struct T(i32); + +struct U(S,T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(String::from("Hello World")), T(0)) + } +} + +fn test_clone_trait() { + let f = U(S(String::from("Hello World")), T(0)); + let c = || { let _ = &f; + //~^ ERROR: `Clone` trait implementation, and drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `f` to be fully captured + let f_1 = f.1; + println!("{:?}", f_1.0); + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn main() { + test_send_trait(); + test_sync_trait(); + test_clone_trait(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs new file mode 100644 index 0000000000000..2c0dbd017548b --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.rs @@ -0,0 +1,67 @@ +// run-rustfix +#![deny(disjoint_capture_migration)] + +use std::thread; + +/* Test Send Trait Migration */ +struct SendPointer (*mut i32); +unsafe impl Send for SendPointer {} + +fn test_send_trait() { + let mut f = 10; + let fptr = SendPointer(&mut f as *mut i32); + thread::spawn(move || unsafe { + //~^ ERROR: `Send` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0 = 20; + }); +} + +/* Test Sync Trait Migration */ +struct CustomInt (*mut i32); +struct SyncPointer (CustomInt); +unsafe impl Sync for SyncPointer {} +unsafe impl Send for CustomInt {} + +fn test_sync_trait() { + let mut f = 10; + let f = CustomInt(&mut f as *mut i32); + let fptr = SyncPointer(f); + thread::spawn(move || unsafe { + //~^ ERROR: `Sync`, `Send` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `fptr` to be fully captured + *fptr.0.0 = 20; + }); +} + +/* Test Clone Trait Migration */ +struct S(String); +struct T(i32); + +struct U(S,T); + +impl Clone for U { + fn clone(&self) -> Self { + U(S(String::from("Hello World")), T(0)) + } +} + +fn test_clone_trait() { + let f = U(S(String::from("Hello World")), T(0)); + let c = || { + //~^ ERROR: `Clone` trait implementation, and drop order affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `f` to be fully captured + let f_1 = f.1; + println!("{:?}", f_1.0); + }; + + let c_clone = c.clone(); + + c_clone(); +} + +fn main() { + test_send_trait(); + test_sync_trait(); + test_clone_trait(); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr new file mode 100644 index 0000000000000..6e3723b8bdb20 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/auto_traits.stderr @@ -0,0 +1,69 @@ +error: `Send` trait implementation affected for closure because of `capture_disjoint_fields` + --> $DIR/auto_traits.rs:13:19 + | +LL | thread::spawn(move || unsafe { + | ___________________^ +LL | | +LL | | +LL | | *fptr.0 = 20; +LL | | }); + | |_____^ + | +note: the lint level is defined here + --> $DIR/auto_traits.rs:2:9 + | +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a dummy let to cause `fptr` to be fully captured + | +LL | thread::spawn(move || { let _ = &fptr; unsafe { +LL | +LL | +LL | *fptr.0 = 20; +LL | } }); + | + +error: `Sync`, `Send` trait implementation affected for closure because of `capture_disjoint_fields` + --> $DIR/auto_traits.rs:30:19 + | +LL | thread::spawn(move || unsafe { + | ___________________^ +LL | | +LL | | +LL | | *fptr.0.0 = 20; +LL | | }); + | |_____^ + | +help: add a dummy let to cause `fptr` to be fully captured + | +LL | thread::spawn(move || { let _ = &fptr; unsafe { +LL | +LL | +LL | *fptr.0.0 = 20; +LL | } }); + | + +error: `Clone` trait implementation, and drop order affected for closure because of `capture_disjoint_fields` + --> $DIR/auto_traits.rs:51:13 + | +LL | let c = || { + | _____________^ +LL | | +LL | | +LL | | let f_1 = f.1; +LL | | println!("{:?}", f_1.0); +LL | | }; + | |_____^ + | +help: add a dummy let to cause `f` to be fully captured + | +LL | let c = || { let _ = &f; +LL | +LL | +LL | let f_1 = f.1; +LL | println!("{:?}", f_1.0); +LL | }; + | + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed index 300f67e8b1e81..3770e93239a8e 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test cases for types that implement a insignificant drop (stlib defined) diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs index a17c70d3e2877..2015ab7e9b8cb 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.rs @@ -1,6 +1,6 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test cases for types that implement a insignificant drop (stlib defined) diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr index 69c12d2bb56c0..69a99f7a53a53 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/insignificant_drop.stderr @@ -14,8 +14,8 @@ LL | | }; note: the lint level is defined here --> $DIR/insignificant_drop.rs:3:9 | -LL | #![deny(disjoint_capture_drop_reorder)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured | LL | let c = || { let _ = (&t, &t1, &t2); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs index 3a6af00254c5a..ee3138ea69ee8 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.rs @@ -1,6 +1,6 @@ // run-pass -#![warn(disjoint_capture_drop_reorder)] +#![warn(disjoint_capture_migration)] fn main() { if let a = "" { diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed index a3e51a2b8e91a..979c023fc53ac 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test the two possible cases for automated migartion using rustfix diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs index 0eb837b688835..c2a700bd9caa0 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.rs @@ -1,5 +1,5 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test the two possible cases for automated migartion using rustfix diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr index e6173217edc2f..a968d3a093b15 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/migrations_rustfix.stderr @@ -12,8 +12,8 @@ LL | | }; note: the lint level is defined here --> $DIR/migrations_rustfix.rs:2:9 | -LL | #![deny(disjoint_capture_drop_reorder)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a dummy let to cause `t` to be fully captured | LL | let c = || { let _ = &t; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed new file mode 100644 index 0000000000000..95463a62185e9 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.fixed @@ -0,0 +1,39 @@ +// run-rustfix + +#![deny(disjoint_capture_migration)] +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo_diverges() -> ! { panic!() } + +fn assert_panics(f: F) where F: FnOnce() { + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { let _ = &f; + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `f` to be fully captured + f.0() + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic(mut t: T) + where T: Fn() -> ! +{ + let as_fn = >::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = >::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = >::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs new file mode 100644 index 0000000000000..fae7fc87c0285 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.rs @@ -0,0 +1,39 @@ +// run-rustfix + +#![deny(disjoint_capture_migration)] +// ignore-wasm32-bare compiled with panic=abort by default + +#![feature(fn_traits)] +#![feature(never_type)] + +use std::panic; + +fn foo_diverges() -> ! { panic!() } + +fn assert_panics(f: F) where F: FnOnce() { + let f = panic::AssertUnwindSafe(f); + let result = panic::catch_unwind(move || { + //~^ ERROR: `UnwindSafe`, `RefUnwindSafe` trait implementation affected for closure because of `capture_disjoint_fields` + //~| HELP: add a dummy let to cause `f` to be fully captured + f.0() + }); + if let Ok(..) = result { + panic!("diverging function returned"); + } +} + +fn test_fn_ptr_panic(mut t: T) + where T: Fn() -> ! +{ + let as_fn = >::call; + assert_panics(|| as_fn(&t, ())); + let as_fn_mut = >::call_mut; + assert_panics(|| as_fn_mut(&mut t, ())); + let as_fn_once = >::call_once; + assert_panics(|| as_fn_once(t, ())); +} + +fn main() { + test_fn_ptr_panic(foo_diverges); + test_fn_ptr_panic(foo_diverges as fn() -> !); +} diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr new file mode 100644 index 0000000000000..bbc8eb9a9cd25 --- /dev/null +++ b/src/test/ui/closures/2229_closure_analysis/migrations/mir_calls_to_shims.stderr @@ -0,0 +1,27 @@ +error: `UnwindSafe`, `RefUnwindSafe` trait implementation affected for closure because of `capture_disjoint_fields` + --> $DIR/mir_calls_to_shims.rs:15:38 + | +LL | let result = panic::catch_unwind(move || { + | ______________________________________^ +LL | | +LL | | +LL | | f.0() +LL | | }); + | |_____^ + | +note: the lint level is defined here + --> $DIR/mir_calls_to_shims.rs:3:9 + | +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: add a dummy let to cause `f` to be fully captured + | +LL | let result = panic::catch_unwind(move || { let _ = &f; +LL | +LL | +LL | f.0() +LL | }); + | + +error: aborting due to previous error + diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs index 73592ce04c28f..420d66fba5e30 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/no_migrations.rs @@ -2,7 +2,7 @@ // Set of test cases that don't need migrations -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] // Copy types as copied by the closure instead of being moved into the closure diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed index b739035c78422..5c93fce92507b 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] #[derive(Debug)] struct Foo(i32); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs index e1f29c9d0e9d8..fb4af00aa0616 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.rs @@ -1,6 +1,6 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] #[derive(Debug)] struct Foo(i32); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr index 7135ded13c256..0cd191e2c98c5 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise.stderr @@ -13,8 +13,8 @@ LL | | }; note: the lint level is defined here --> $DIR/precise.rs:3:9 | -LL | #![deny(disjoint_capture_drop_reorder)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a dummy let to cause `t` to be fully captured | LL | let c = || { let _ = &t; diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs index 8af48501ca295..e3a7220bf09d2 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/precise_no_migrations.rs @@ -1,6 +1,6 @@ // run-pass -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] #[derive(Debug)] struct Foo(i32); diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed index e1b212153f431..1fa0fb3db2f8d 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test cases for types that implement a significant drop (user defined) diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs index 106b293351515..1f0efbe1ebc43 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.rs @@ -1,5 +1,5 @@ // run-rustfix -#![deny(disjoint_capture_drop_reorder)] +#![deny(disjoint_capture_migration)] //~^ NOTE: the lint level is defined here // Test cases for types that implement a significant drop (user defined) diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr index ee29fe1306059..91e75ffb81a96 100644 --- a/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr +++ b/src/test/ui/closures/2229_closure_analysis/migrations/significant_drop.stderr @@ -14,8 +14,8 @@ LL | | }; note: the lint level is defined here --> $DIR/significant_drop.rs:2:9 | -LL | #![deny(disjoint_capture_drop_reorder)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #![deny(disjoint_capture_migration)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a dummy let to cause `t`, `t1`, `t2` to be fully captured | LL | let c = || { let _ = (&t, &t1, &t2);