diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a48db2cc855c0..e789f501d4b78 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,4 +1,5 @@ use rustc_errors::Applicability; +use rustc_hir::LangItem; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; @@ -8,7 +9,8 @@ use rustc_middle::ty::adjustment::{ PointerCoercion, }; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, sym}; +use rustc_span::{DUMMY_SP, Span, sym}; +use rustc_trait_selection::infer::InferCtxtExt; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -285,9 +287,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Clear previous flag; after a pointer indirection it does not apply any more. inside_union = false; } - if source.is_union() { - inside_union = true; - } // Fix up the autoderefs. Autorefs can only occur immediately preceding // overloaded place ops, and will be fixed by them in order to get // the correct region. @@ -315,14 +314,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; *deref = OverloadedDeref { mutbl, span: deref.span }; self.enforce_context_effects(None, expr.span, method.def_id, method.args); - // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). + // If this is a union field, also throw an error for `DerefMut` of non `BikeshedGuaranteedNoDrop` (see RFC 2514). // This helps avoid accidental drops. if inside_union - && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop()) + && !self + .infcx + .type_implements_trait( + self.tcx.require_lang_item( + LangItem::BikeshedGuaranteedNoDrop, + DUMMY_SP, + ), + [adjustment.target], + self.param_env, + ) + .must_apply_considering_regions() { self.dcx().struct_span_err( expr.span, - "not automatically applying `DerefMut` on `ManuallyDrop` union field", + "not automatically applying `DerefMut` through union field to target that might have `Drop` glue", ) .with_help( "writing to this reference calls the destructor for the old value", @@ -335,6 +344,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); } + // Now that any autoderef of this expression component is complete, if it resolved to a + // `union` then remember we're inside a `union` as we iterate over subsequent expression + // components. If this test were performed prior to autoderef resolution, it would only + // detect expression components of the (owned) `union` type and not references thereto. + // See . + if source.is_union() { + inside_union = true; + } match expr.kind { hir::ExprKind::Index(base_expr, ..) => { diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs index 510c546f82a4e..893e72e16d75f 100644 --- a/compiler/rustc_middle/src/ty/adt.rs +++ b/compiler/rustc_middle/src/ty/adt.rs @@ -59,6 +59,8 @@ bitflags::bitflags! { const IS_PIN = 1 << 11; /// Indicates whether the type is `#[pin_project]`. const IS_PIN_PROJECT = 1 << 12; + /// Indicates whether the type is `MaybeUninit`. + const IS_MAYBE_UNINIT = 1 << 13; } } rustc_data_structures::external_bitflags_debug! { AdtFlags } @@ -224,6 +226,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef> for AdtDef<'tcx> { self.is_manually_drop() } + fn is_maybe_uninit(self) -> bool { + self.is_maybe_uninit() + } + fn all_field_tys( self, tcx: TyCtxt<'tcx>, @@ -315,6 +321,9 @@ impl AdtDefData { if tcx.is_lang_item(did, LangItem::ManuallyDrop) { flags |= AdtFlags::IS_MANUALLY_DROP; } + if tcx.is_lang_item(did, LangItem::MaybeUninit) { + flags |= AdtFlags::IS_MAYBE_UNINIT; + } if tcx.is_lang_item(did, LangItem::UnsafeCell) { flags |= AdtFlags::IS_UNSAFE_CELL; } @@ -439,6 +448,12 @@ impl<'tcx> AdtDef<'tcx> { self.flags().contains(AdtFlags::IS_MANUALLY_DROP) } + /// Returns `true` if this is `ManuallyDrop`. + #[inline] + pub fn is_maybe_uninit(self) -> bool { + self.flags().contains(AdtFlags::IS_MAYBE_UNINIT) + } + /// Returns `true` if this is `Pin`. #[inline] pub fn is_pin(self) -> bool { diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index 003841f3af3fd..993ceefd6251f 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -701,8 +701,8 @@ where match ty.kind() { // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. ty::Ref(..) => {} - // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. - ty::Adt(def, _) if def.is_manually_drop() => {} + // `ManuallyDrop` and `MaybeUninit` always implement `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() || def.is_maybe_uninit() => {} // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if // their constituent types implement `BikeshedGuaranteedNoDrop`. ty::Tuple(tys) => { diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 4f65b30775ed0..c1ef8cf11831a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -1257,8 +1257,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match *self_ty.skip_binder().kind() { // `&mut T` and `&T` always implement `BikeshedGuaranteedNoDrop`. ty::Ref(..) => {} - // `ManuallyDrop` always implements `BikeshedGuaranteedNoDrop`. - ty::Adt(def, _) if def.is_manually_drop() => {} + // `ManuallyDrop` and `MaybeUninit` always implement `BikeshedGuaranteedNoDrop`. + ty::Adt(def, _) if def.is_manually_drop() || def.is_maybe_uninit() => {} // Arrays and tuples implement `BikeshedGuaranteedNoDrop` only if // their constituent types implement `BikeshedGuaranteedNoDrop`. ty::Tuple(tys) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 89cb236d38c6d..bc9d27c0bb2ff 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -566,6 +566,7 @@ pub trait AdtDef: Copy + Debug + Hash + Eq { fn is_phantom_data(self) -> bool; fn is_manually_drop(self) -> bool; + fn is_maybe_uninit(self) -> bool; // FIXME: perhaps use `all_fields` and expose `FieldDef`. fn all_field_tys(self, interner: I) -> ty::EarlyBinder>; diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 57416455e9de8..1690a528a34a2 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -496,6 +496,7 @@ impl Copy for &T {} /// Implemented for: /// * `&T`, `&mut T` for all `T`, /// * `ManuallyDrop` for all `T`, +/// * `MaybeUninit` for all `T`, /// * tuples and arrays whose elements implement `BikeshedGuaranteedNoDrop`, /// * or otherwise, all types that are `Copy`. /// diff --git a/tests/ui/union/union-deref.rs b/tests/ui/union/union-deref.rs index 02f9bf2c413f4..a3c258f7e2f60 100644 --- a/tests/ui/union/union-deref.rs +++ b/tests/ui/union/union-deref.rs @@ -10,17 +10,32 @@ 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` through union field to target that might have `Drop` glue 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` through union field to target that might have `Drop` glue 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` through union field to target that might have `Drop` glue + + unsafe { (*(&mut u).f).0 = Vec::new() }; // explicit deref, this compiles + unsafe { (&mut u).f.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue + unsafe { &mut (*(&mut u).f).0 }; // explicit deref, this compiles + unsafe { &mut (&mut u).f.0 }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue + unsafe { (*(&mut u).f).0.push(0) }; // explicit deref, this compiles + unsafe { (&mut u).f.0.push(0) }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue + 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` through union field to target that might have `Drop` glue 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` through union field to target that might have `Drop` glue 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` through union field to target that might have `Drop` glue + + unsafe { (*(&mut u).f.0).0 = Vec::new() }; // explicit deref, this compiles + unsafe { (&mut u).f.0.0 = Vec::new() }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue + unsafe { &mut (*(&mut u).f.0).0 }; // explicit deref, this compiles + unsafe { &mut (&mut u).f.0.0 }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue + unsafe { (*(&mut u).f.0).0.push(0) }; // explicit deref, this compiles + unsafe { (&mut u).f.0.0.push(0) }; //~ERROR not automatically applying `DerefMut` through union field to target that might have `Drop` glue } diff --git a/tests/ui/union/union-deref.stderr b/tests/ui/union/union-deref.stderr index 38fce568ae78c..7a9630b6bbc91 100644 --- a/tests/ui/union/union-deref.stderr +++ b/tests/ui/union/union-deref.stderr @@ -1,4 +1,4 @@ -error: not automatically applying `DerefMut` on `ManuallyDrop` union field +error: not automatically applying `DerefMut` through union field to target that might have `Drop` glue --> $DIR/union-deref.rs:13: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` through union field to target that might have `Drop` glue --> $DIR/union-deref.rs:15: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` through union field to target that might have `Drop` glue --> $DIR/union-deref.rs:17:14 | LL | unsafe { u.f.0.push(0) }; @@ -25,8 +25,35 @@ 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 - --> $DIR/union-deref.rs:21:14 +error: not automatically applying `DerefMut` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:20:14 + | +LL | unsafe { (&mut 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` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:22:19 + | +LL | unsafe { &mut (&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` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:24:14 + | +LL | unsafe { (&mut 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` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:29:14 | LL | unsafe { u.f.0.0 = Vec::new() }; | ^^^^^ @@ -34,8 +61,8 @@ 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 - --> $DIR/union-deref.rs:23:19 +error: not automatically applying `DerefMut` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:31:19 | LL | unsafe { &mut u.f.0.0 }; | ^^^^^ @@ -43,8 +70,8 @@ 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 - --> $DIR/union-deref.rs:25:14 +error: not automatically applying `DerefMut` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:33:14 | LL | unsafe { u.f.0.0.push(0) }; | ^^^^^ @@ -52,5 +79,32 @@ LL | unsafe { u.f.0.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: aborting due to 6 previous errors +error: not automatically applying `DerefMut` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:36:14 + | +LL | unsafe { (&mut 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` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:38:19 + | +LL | unsafe { &mut (&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` through union field to target that might have `Drop` glue + --> $DIR/union-deref.rs:40:14 + | +LL | unsafe { (&mut u).f.0.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: aborting due to 12 previous errors