diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 323f5a368fc09..0466cbd803226 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -442,6 +442,8 @@ declare_features! ( (active, lint_reasons, "1.31.0", Some(54503), None), /// Give access to additional metadata about declarative macro meta-variables. (active, macro_metavar_expr, "1.61.0", Some(83527), None), + /// Allows `#[manually_drop]` on type definitions. + (active, manually_drop_attr, "1.64.0", Some(100344), None), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (active, marker_trait_attr, "1.30.0", Some(29864), None), /// A minimal, sound subset of specialization intended to be used by the diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index af56a0b245987..6a57ae91318dc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -480,6 +480,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ deprecated_safe, Normal, template!(List: r#"since = "version", note = "...""#), ErrorFollowing, experimental!(deprecated_safe), ), + // lang-team MCP 135 + gated!( + manually_drop, Normal, template!(Word), WarnFollowing, manually_drop_attr, experimental!(manually_drop), + ), // `#[collapse_debuginfo]` gated!( diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 47eace961be55..45327cc7e8050 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -112,8 +112,10 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b allowed_union_field(*elem, tcx, param_env) } _ => { - // Fallback case: allow `ManuallyDrop` and things that are `Copy`. - ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop()) + // Fallback case: if there is no destructor (including field drop glue), because it is + // `Copy` or is `#[manually_drop]` with no `Drop`, then allow it. + ty.ty_adt_def() + .is_some_and(|adt_def| adt_def.is_manually_drop() && !adt_def.has_dtor(tcx)) || ty.is_copy_modulo_regions(tcx, param_env) } } diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ae0df5aa8f1cf..f0d8fc6c732f5 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -330,12 +330,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. + // + // FIXME: This is not correct for `#[manually_drop]` in general, only when the struct is + // a smart pointer whose pointee is at the same address, and whose pointee implements `Drop`. + // Instead of checking for `#[manually_drop]`, this should likely be a more restricted check + // for such types, or else union really should special-case and only permit `ManuallyDrop`, and + // not `#[manually_drop]` types in general. if inside_union && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) { let mut err = self.tcx.sess.struct_span_err( expr.span, - "not automatically applying `DerefMut` on `ManuallyDrop` union field", + "not automatically applying `DerefMut` on manually dropped union field", ); err.help( "writing to this reference calls the destructor for the old value", diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 099a784511827..ce4d4743df698 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -241,7 +241,7 @@ impl AdtDefData { if Some(did) == tcx.lang_items().owned_box() { flags |= AdtFlags::IS_BOX; } - if Some(did) == tcx.lang_items().manually_drop() { + if tcx.has_attr(did, sym::manually_drop) { flags |= AdtFlags::IS_MANUALLY_DROP; } if Some(did) == tcx.lang_items().unsafe_cell_type() { diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index 7836ae2e7b76f..e3757e88a12c9 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -443,8 +443,7 @@ where }); } - let skip_contents = - adt.is_union() || Some(adt.did()) == self.tcx().lang_items().manually_drop(); + let skip_contents = adt.is_union() || adt.is_manually_drop(); let contents_drop = if skip_contents { (self.succ, self.unwind) } else { diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 238ec9ca30f70..b086705c10019 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -193,6 +193,7 @@ impl CheckAttrVisitor<'_> { sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target), sym::deprecated => self.check_deprecated(hir_id, attr, span, target), sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target), + sym::manually_drop => self.check_manually_drop(hir_id, attr, span, target), sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod), sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target), sym::macro_export => self.check_macro_export(hir_id, attr, target), @@ -2091,6 +2092,17 @@ impl CheckAttrVisitor<'_> { } } + fn check_manually_drop(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) { + if !matches!(target, Target::Struct | Target::Enum) { + self.tcx.emit_spanned_lint( + UNUSED_ATTRIBUTES, + hir_id, + attr.span, + errors::ManuallyDropShouldBeAppliedToStructEnum { defn_span: span }, + ); + } + } + fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) { if target != Target::Fn { self.tcx.emit_spanned_lint( diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index 68b098e3457b7..58ed983862b50 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -639,6 +639,13 @@ pub struct MacroUse { pub name: Symbol, } +#[derive(LintDiagnostic)] +#[diag(passes_should_be_applied_to_struct_enum)] +pub struct ManuallyDropShouldBeAppliedToStructEnum { + #[label] + pub defn_span: Span, +} + #[derive(LintDiagnostic)] #[diag(passes_macro_export)] pub struct MacroExport; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f1119214be44d..0637660b106e8 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -902,6 +902,7 @@ symbols! { main, managed_boxes, manually_drop, + manually_drop_attr, map, marker, marker_trait_attr, diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 455b53bfb7d8f..f33b2f493855a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -49,8 +49,9 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { } ty::Adt(def, _) => { - if Some(def.did()) == tcx.lang_items().manually_drop() { - // `ManuallyDrop` never has a dtor. + if def.is_manually_drop() && !def.has_dtor(tcx) { + // A `#[manually_drop]` type without a Drop impl (e.g. `ManuallyDrop`) + // does not run any code at all when dropped. true } else { // Other types might. Moreover, PhantomData doesn't diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index cd1475391a4d9..3b744083707e0 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -219,10 +219,7 @@ fn drop_tys_helper<'tcx>( } let adt_components = move |adt_def: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>| { - if adt_def.is_manually_drop() { - debug!("drop_tys_helper: `{:?}` is manually drop", adt_def); - Ok(Vec::new()) - } else if let Some(dtor_info) = adt_has_dtor(adt_def) { + if let Some(dtor_info) = adt_has_dtor(adt_def) { match dtor_info { DtorType::Significant => { debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def); @@ -237,6 +234,9 @@ fn drop_tys_helper<'tcx>( Ok(substs.types().collect()) } } + } else if adt_def.is_manually_drop() { + debug!("drop_tys_helper: `{:?}` is manually drop", adt_def); + Ok(Vec::new()) } else if adt_def.is_union() { debug!("drop_tys_helper: `{:?}` is a union", adt_def); Ok(Vec::new()) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dc0702c467a4e..5784e30b253ed 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -214,6 +214,7 @@ #![feature(lang_items)] #![feature(link_llvm_intrinsics)] #![feature(macro_metavar_expr)] +#![cfg_attr(not(bootstrap), feature(manually_drop_attr))] #![feature(min_specialization)] #![feature(must_not_suspend)] #![feature(negative_impls)] diff --git a/library/core/src/mem/manually_drop.rs b/library/core/src/mem/manually_drop.rs index 3d719afe49e4a..f5a9ad20c0aa0 100644 --- a/library/core/src/mem/manually_drop.rs +++ b/library/core/src/mem/manually_drop.rs @@ -44,7 +44,8 @@ use crate::ptr; /// [`mem::zeroed`]: crate::mem::zeroed /// [`MaybeUninit`]: crate::mem::MaybeUninit #[stable(feature = "manually_drop", since = "1.20.0")] -#[lang = "manually_drop"] +#[cfg_attr(bootstrap, lang = "manually_drop")] +#[cfg_attr(not(bootstrap), manually_drop)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct ManuallyDrop { diff --git a/src/doc/unstable-book/src/language-features/manually-drop-attr.md b/src/doc/unstable-book/src/language-features/manually-drop-attr.md new file mode 100644 index 0000000000000..602240d8d3569 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/manually-drop-attr.md @@ -0,0 +1,36 @@ +# `manually_drop_attr` + +The tracking issue for this feature is: [#100344] + +[#100344]: https://github.com/rust-lang/rust/issues/100344 + +The `manually_drop_attr` feature enables the `#[manually_drop]` attribute, which disables the drop glue for the type it is applied to. + +For example, `std::mem::ManuallyDrop` is implemented as follows: + +```rs +#[manually_drop] +struct ManuallyDrop(T); +``` + +But you can also use the attribute to change the order in which fields are dropped, by overriding `Drop`: + +```rs +/// This struct changes the order in which `x` and `y` are dropped from the default. +#[manually_drop] +struct MyStruct { + x: String, + y: String, +} + +impl Drop for MyStruct { + fn drop(&mut self) { + unsafe { + std::ptr::drop_in_place(&mut self.y); + std::ptr::drop_in_place(&mut self.x); + } + } +} +``` + +This can be useful in combination with `repr(C)`, to decouple the layout from the destruction order. See MCP [#135](https://github.com/rust-lang/lang-team/issues/135). diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 944a33cc3e53f..81b6c26361783 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -112,6 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { } if let ty::Adt(def, _) = arg_ty.kind() { + // FIXME: This is not correct with `#[manually_drop]`, as that is just like any other type. if def.is_manually_drop() { continue; } diff --git a/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.rs b/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.rs new file mode 100644 index 0000000000000..835921ac6af9b --- /dev/null +++ b/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.rs @@ -0,0 +1,5 @@ +#[manually_drop] +//~^ ERROR the `#[manually_drop]` attribute is an experimental feature +struct Foo {} + +fn main() {} diff --git a/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.stderr b/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.stderr new file mode 100644 index 0000000000000..e105179cb2743 --- /dev/null +++ b/tests/ui/manually_drop_attr/feature-gate-manually_drop_attr.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[manually_drop]` attribute is an experimental feature + --> $DIR/feature-gate-manually_drop_attr.rs:1:1 + | +LL | #[manually_drop] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #100344 for more information + = help: add `#![feature(manually_drop_attr)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/manually_drop_attr/manually_drop-bad-item.rs b/tests/ui/manually_drop_attr/manually_drop-bad-item.rs new file mode 100644 index 0000000000000..502fc2862cf0e --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-bad-item.rs @@ -0,0 +1,10 @@ +#![feature(manually_drop_attr)] +#![forbid(unused_attributes)] +#![manually_drop] +//~^ ERROR attribute should be applied to a struct or enum + +#[manually_drop] +//~^ ERROR attribute should be applied to a struct or enum +fn foo() {} + +fn main() {} diff --git a/tests/ui/manually_drop_attr/manually_drop-bad-item.stderr b/tests/ui/manually_drop_attr/manually_drop-bad-item.stderr new file mode 100644 index 0000000000000..890d45325ca57 --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-bad-item.stderr @@ -0,0 +1,23 @@ +error: attribute should be applied to a struct or enum + --> $DIR/manually_drop-bad-item.rs:6:1 + | +LL | #[manually_drop] + | ^^^^^^^^^^^^^^^^ +LL | +LL | fn foo() {} + | ----------- not a struct or enum + | +note: the lint level is defined here + --> $DIR/manually_drop-bad-item.rs:2:11 + | +LL | #![forbid(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: attribute should be applied to a struct or enum + --> $DIR/manually_drop-bad-item.rs:3:1 + | +LL | #![manually_drop] + | ^^^^^^^^^^^^^^^^^ not a struct or enum + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manually_drop_attr/manually_drop-destructor.rs b/tests/ui/manually_drop_attr/manually_drop-destructor.rs new file mode 100644 index 0000000000000..a9138ebb70224 --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-destructor.rs @@ -0,0 +1,83 @@ +//! A test of `#[manually_drop]` on a type that *does* have a `Drop` impl. +//! +//! The mirror image of `manually_drop-nodestructor.rs` +#![feature(manually_drop_attr)] +// run-pass +extern crate core; +use core::cell::Cell; + +struct DropCounter<'a>(&'a Cell); +impl<'a> Drop for DropCounter<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } +} + +#[manually_drop] +struct ManuallyDropped<'a> { + field_1: DropCounter<'a>, + field_2: DropCounter<'a>, +} + +impl<'a> Drop for ManuallyDropped<'a> { + fn drop(&mut self) { + // just do a LITTLE dropping. + unsafe { + core::ptr::drop_in_place(&mut self.field_1) + } + } +} + +#[manually_drop] +enum ManuallyDroppedEnum<'a> { + _A, + B(DropCounter<'a>, DropCounter<'a>), +} + +impl<'a> Drop for ManuallyDroppedEnum<'a> { + fn drop(&mut self) { + // just do a LITTLE dropping. + if let ManuallyDroppedEnum::B(a, _) = self { + unsafe { + core::ptr::drop_in_place(a); + } + } + } +} + +/// Dropping a `#[manually_drop]` struct does not implicitly drop its fields. +/// +/// (Though it does run `Drop`, which can choose to drop them explicitly.) +fn test_destruction() { + let counter = Cell::new(0); + core::mem::drop(ManuallyDropped { + field_1: DropCounter(&counter), + field_2: DropCounter(&counter), + }); + // We only run the drop specifically requested in the Drop impl. + assert_eq!(counter.get(), 1); + assert!(core::mem::needs_drop::()); + + core::mem::drop(ManuallyDroppedEnum::B(DropCounter(&counter), DropCounter(&counter))); + assert_eq!(counter.get(), 2); + assert!(core::mem::needs_drop::()); + +} + +/// Assignment does still drop the fields. +fn test_assignment() { + let counter = Cell::new(0); + let mut manually_dropped = ManuallyDropped { + field_1: DropCounter(&counter), + field_2: DropCounter(&counter), + }; + assert_eq!(counter.get(), 0); + manually_dropped.field_1 = DropCounter(&counter); + manually_dropped.field_2 = DropCounter(&counter); + assert_eq!(counter.get(), 2); +} + +fn main() { + test_destruction(); + test_assignment(); +} diff --git a/tests/ui/manually_drop_attr/manually_drop-dropck.rs b/tests/ui/manually_drop_attr/manually_drop-dropck.rs new file mode 100644 index 0000000000000..95a108735564c --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-dropck.rs @@ -0,0 +1,38 @@ +//! The drop checker only complains about a `#[manually_drop]` type if it _itself_ defines `Drop`. + +// FIXME: this does test dropck, does it also test needs_drop? + +#![feature(manually_drop_attr)] + + +// For example, this is absolutely fine: + +#[manually_drop] +struct ManuallyDrop(T); + +fn drop_out_of_order_ok(x: T) { + let mut manually_dropped = ManuallyDrop(None); + // x will be dropped before manually_dropped. + let x = x; + // ... but this is still fine, because it doesn't have logic on Drop. + manually_dropped.0 = Some(&x); +} + +// ... but this is not: + +#[manually_drop] +struct ManuallyDropWithDestructor(T); +impl Drop for ManuallyDropWithDestructor { + fn drop(&mut self) { + // maybe we read self.0 here! + } +} + +fn drop_out_of_order_not_ok(x: T) { + let mut manually_dropped_bad = ManuallyDropWithDestructor(None); + let x = x; + manually_dropped_bad.0 = Some(&x); + //~^ ERROR `x` does not live long enough +} + +fn main() {} diff --git a/tests/ui/manually_drop_attr/manually_drop-dropck.stderr b/tests/ui/manually_drop_attr/manually_drop-dropck.stderr new file mode 100644 index 0000000000000..52063baca6d08 --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-dropck.stderr @@ -0,0 +1,19 @@ +error[E0597]: `x` does not live long enough + --> $DIR/manually_drop-dropck.rs:34:35 + | +LL | let x = x; + | - binding `x` declared here +LL | manually_dropped_bad.0 = Some(&x); + | ^^ borrowed value does not live long enough +LL | +LL | } + | - + | | + | `x` dropped here while still borrowed + | borrow might be used here, when `manually_dropped_bad` is dropped and runs the `Drop` code for type `ManuallyDropWithDestructor` + | + = note: values in a scope are dropped in the opposite order they are defined + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0597`. diff --git a/tests/ui/manually_drop_attr/manually_drop-in-union.rs b/tests/ui/manually_drop_attr/manually_drop-in-union.rs new file mode 100644 index 0000000000000..cd48d89c6ec37 --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-in-union.rs @@ -0,0 +1,18 @@ +//! A `#[manually_drop]` type with a destructor is still not allowed inside a union! +#![feature(manually_drop_attr)] + +extern crate core; + +#[manually_drop] +struct ManuallyDropHasDrop; + +impl Drop for ManuallyDropHasDrop { + fn drop(&mut self) {} +} + +union MyUnion { + x: ManuallyDropHasDrop, + //~^ ERROR: unions cannot contain fields that may need dropping +} + +fn main() {} diff --git a/tests/ui/manually_drop_attr/manually_drop-in-union.stderr b/tests/ui/manually_drop_attr/manually_drop-in-union.stderr new file mode 100644 index 0000000000000..7c2ac50dee17e --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-in-union.stderr @@ -0,0 +1,15 @@ +error[E0740]: unions cannot contain fields that may need dropping + --> $DIR/manually_drop-in-union.rs:14:5 + | +LL | x: ManuallyDropHasDrop, + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type +help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped + | +LL | x: std::mem::ManuallyDrop, + | +++++++++++++++++++++++ + + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0740`. diff --git a/tests/ui/manually_drop_attr/manually_drop-nodestructor.rs b/tests/ui/manually_drop_attr/manually_drop-nodestructor.rs new file mode 100644 index 0000000000000..da0597f398455 --- /dev/null +++ b/tests/ui/manually_drop_attr/manually_drop-nodestructor.rs @@ -0,0 +1,60 @@ +//! A test of `#[manually_drop]` on a type that *doesn't* have a `Drop` impl. +//! +//! The mirror image of `manually_drop-destructor.rs` + +#![feature(manually_drop_attr)] +// run-pass +extern crate core; +use core::cell::Cell; + +struct DropCounter<'a>(&'a Cell); +impl<'a> Drop for DropCounter<'a> { + fn drop(&mut self) { + self.0.set(self.0.get() + 1); + } +} + +#[manually_drop] +struct ManuallyDropped<'a> { + field_1: DropCounter<'a>, + field_2: DropCounter<'a>, +} + +#[manually_drop] +enum ManuallyDroppedEnum<'a> { + _A, + B(DropCounter<'a>, DropCounter<'a>), +} + +/// Dropping a `#[manually_drop]` type does not implicitly drop its fields. +fn test_destruction() { + let counter = Cell::new(0); + core::mem::drop(ManuallyDropped { + field_1: DropCounter(&counter), + field_2: DropCounter(&counter), + }); + assert_eq!(counter.get(), 0); + assert!(!core::mem::needs_drop::()); + + core::mem::drop(ManuallyDroppedEnum::B(DropCounter(&counter), DropCounter(&counter))); + assert_eq!(counter.get(), 0); + assert!(!core::mem::needs_drop::()); +} + +/// Assignment does still drop the fields. +fn test_assignment() { + let counter = Cell::new(0); + let mut manually_dropped = ManuallyDropped { + field_1: DropCounter(&counter), + field_2: DropCounter(&counter), + }; + assert_eq!(counter.get(), 0); + manually_dropped.field_1 = DropCounter(&counter); + manually_dropped.field_2 = DropCounter(&counter); + assert_eq!(counter.get(), 2); +} + +fn main() { + test_destruction(); + test_assignment(); +} diff --git a/tests/ui/union/union-deref.mirunsafeck.stderr b/tests/ui/union/union-deref.mirunsafeck.stderr index be5e60ab88a59..ce3571deb1c8b 100644 --- a/tests/ui/union/union-deref.mirunsafeck.stderr +++ b/tests/ui/union/union-deref.mirunsafeck.stderr @@ -1,4 +1,4 @@ -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:16:14 | LL | unsafe { u.f.0 = Vec::new() }; @@ -7,7 +7,7 @@ LL | unsafe { u.f.0 = Vec::new() }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:18:19 | LL | unsafe { &mut u.f.0 }; @@ -16,7 +16,7 @@ LL | unsafe { &mut u.f.0 }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:20:14 | LL | unsafe { u.f.0.push(0) }; @@ -25,7 +25,7 @@ LL | unsafe { u.f.0.push(0) }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:24:14 | LL | unsafe { u.f.0.0 = Vec::new() }; @@ -34,7 +34,7 @@ LL | unsafe { u.f.0.0 = Vec::new() }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:26:19 | LL | unsafe { &mut u.f.0.0 }; @@ -43,7 +43,7 @@ LL | unsafe { &mut u.f.0.0 }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:28:14 | LL | unsafe { u.f.0.0.push(0) }; diff --git a/tests/ui/union/union-deref.rs b/tests/ui/union/union-deref.rs index 5aa28d93f96ed..b8afc9793d921 100644 --- a/tests/ui/union/union-deref.rs +++ b/tests/ui/union/union-deref.rs @@ -13,17 +13,17 @@ union U2 { x:(), f: (ManuallyDrop<(T,)>,) } fn main() { let mut u : U1> = U1 { x: () }; unsafe { (*u.f).0 = Vec::new() }; // explicit deref, this compiles - unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { u.f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on manually dropped union field unsafe { &mut (*u.f).0 }; // explicit deref, this compiles - unsafe { &mut u.f.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut u.f.0 }; //~ERROR not automatically applying `DerefMut` on manually dropped union field unsafe { (*u.f).0.push(0) }; // explicit deref, this compiles - unsafe { u.f.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { u.f.0.push(0) }; //~ERROR not automatically applying `DerefMut` on manually dropped union field let mut u : U2> = U2 { x: () }; unsafe { (*u.f.0).0 = Vec::new() }; // explicit deref, this compiles - unsafe { u.f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { u.f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` on manually dropped union field unsafe { &mut (*u.f.0).0 }; // explicit deref, this compiles - unsafe { &mut u.f.0.0 }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { &mut u.f.0.0 }; //~ERROR not automatically applying `DerefMut` on manually dropped union field unsafe { (*u.f.0).0.push(0) }; // explicit deref, this compiles - unsafe { u.f.0.0.push(0) }; //~ERROR not automatically applying `DerefMut` on `ManuallyDrop` union field + unsafe { u.f.0.0.push(0) }; //~ERROR not automatically applying `DerefMut` on manually dropped union field } diff --git a/tests/ui/union/union-deref.thirunsafeck.stderr b/tests/ui/union/union-deref.thirunsafeck.stderr index be5e60ab88a59..ce3571deb1c8b 100644 --- a/tests/ui/union/union-deref.thirunsafeck.stderr +++ b/tests/ui/union/union-deref.thirunsafeck.stderr @@ -1,4 +1,4 @@ -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:16:14 | LL | unsafe { u.f.0 = Vec::new() }; @@ -7,7 +7,7 @@ LL | unsafe { u.f.0 = Vec::new() }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:18:19 | LL | unsafe { &mut u.f.0 }; @@ -16,7 +16,7 @@ LL | unsafe { &mut u.f.0 }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:20:14 | LL | unsafe { u.f.0.push(0) }; @@ -25,7 +25,7 @@ LL | unsafe { u.f.0.push(0) }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:24:14 | LL | unsafe { u.f.0.0 = Vec::new() }; @@ -34,7 +34,7 @@ LL | unsafe { u.f.0.0 = Vec::new() }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:26:19 | LL | unsafe { &mut u.f.0.0 }; @@ -43,7 +43,7 @@ LL | unsafe { &mut u.f.0.0 }; = help: writing to this reference calls the destructor for the old value = help: add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` on manually dropped union field --> $DIR/union-deref.rs:28:14 | LL | unsafe { u.f.0.0.push(0) };