From 91034adf30b3055e489d59f4483ed5def9cc9f19 Mon Sep 17 00:00:00 2001
From: Jack Wrenn <jack@wrenn.fyi>
Date: Fri, 28 Feb 2025 16:32:06 +0000
Subject: [PATCH] Do not require that unsafe fields lack drop glue

Instead, we adopt the position that introducing an `unsafe` field
itself carries a safety invariant: that if you assign an invariant
to that field weaker than what the field's destructor requires,
you must ensure that field is in a droppable state in your
destructor.

See:
- https://github.com/rust-lang/rfcs/pull/3458#discussion_r1971676100
- https://rust-lang.zulipchat.com/#narrow/channel/213817-t-lang/topic/unsafe.20fields.20RFC/near/502113897
---
 compiler/rustc_hir_analysis/messages.ftl      |  7 ----
 .../rustc_hir_analysis/src/check/check.rs     | 32 -------------------
 compiler/rustc_hir_analysis/src/errors.rs     | 23 -------------
 library/core/src/marker.rs                    |  4 +--
 tests/ui/unsafe-fields/unsafe-fields.rs       |  2 +-
 tests/ui/unsafe-fields/unsafe-fields.stderr   | 17 ++--------
 6 files changed, 5 insertions(+), 80 deletions(-)

diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 3f75cce0092..fe2254faf61 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -278,13 +278,6 @@ hir_analysis_invalid_union_field =
 hir_analysis_invalid_union_field_sugg =
     wrap the field type in `ManuallyDrop<...>`
 
-hir_analysis_invalid_unsafe_field =
-    field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe
-    .note = unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
-
-hir_analysis_invalid_unsafe_field_sugg =
-    wrap the field type in `ManuallyDrop<...>`
-
 hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
     .label = const parameter declared here
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f2331f3fd8e..25dc6b7954b 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -70,7 +70,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
     check_transparent(tcx, def);
     check_packed(tcx, span, def);
-    check_unsafe_fields(tcx, def_id);
 }
 
 fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
@@ -144,36 +143,6 @@ fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> b
     true
 }
 
-/// Check that the unsafe fields do not need dropping.
-fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) {
-    let span = tcx.def_span(item_def_id);
-    let def = tcx.adt_def(item_def_id);
-
-    let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
-    let args = ty::GenericArgs::identity_for_item(tcx, item_def_id);
-
-    for field in def.all_fields() {
-        if !field.safety.is_unsafe() {
-            continue;
-        }
-
-        if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) {
-            let hir::Node::Field(field) = tcx.hir_node_by_def_id(field.did.expect_local()) else {
-                unreachable!("field has to correspond to hir field")
-            };
-            let ty_span = field.ty.span;
-            tcx.dcx().emit_err(errors::InvalidUnsafeField {
-                field_span: field.span,
-                sugg: errors::InvalidUnsafeFieldSuggestion {
-                    lo: ty_span.shrink_to_lo(),
-                    hi: ty_span.shrink_to_hi(),
-                },
-                note: (),
-            });
-        }
-    }
-}
-
 /// Check that a `static` is inhabited.
 fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     // Make sure statics are inhabited.
@@ -1517,7 +1486,6 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
     detect_discriminant_duplicate(tcx, def);
     check_transparent(tcx, def);
-    check_unsafe_fields(tcx, def_id);
 }
 
 /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 852533ff5c9..d000314974c 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -710,17 +710,6 @@ pub(crate) struct InvalidUnionField {
     pub note: (),
 }
 
-#[derive(Diagnostic)]
-#[diag(hir_analysis_invalid_unsafe_field, code = E0740)]
-pub(crate) struct InvalidUnsafeField {
-    #[primary_span]
-    pub field_span: Span,
-    #[subdiagnostic]
-    pub sugg: InvalidUnsafeFieldSuggestion,
-    #[note]
-    pub note: (),
-}
-
 #[derive(Diagnostic)]
 #[diag(hir_analysis_return_type_notation_on_non_rpitit)]
 pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> {
@@ -742,18 +731,6 @@ pub(crate) struct InvalidUnionFieldSuggestion {
     pub hi: Span,
 }
 
-#[derive(Subdiagnostic)]
-#[multipart_suggestion(
-    hir_analysis_invalid_unsafe_field_sugg,
-    applicability = "machine-applicable"
-)]
-pub(crate) struct InvalidUnsafeFieldSuggestion {
-    #[suggestion_part(code = "std::mem::ManuallyDrop<")]
-    pub lo: Span,
-    #[suggestion_part(code = ">")]
-    pub hi: Span,
-}
-
 #[derive(Diagnostic)]
 #[diag(hir_analysis_return_type_notation_equality_bound)]
 pub(crate) struct ReturnTypeNotationEqualityBound {
diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs
index b0571bf7247..e2dd813981d 100644
--- a/library/core/src/marker.rs
+++ b/library/core/src/marker.rs
@@ -453,8 +453,8 @@ impl Copy for ! {}
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<T: ?Sized> Copy for &T {}
 
-/// Marker trait for the types that are allowed in union fields, unsafe fields,
-/// and unsafe binder types.
+/// Marker trait for the types that are allowed in union fields and unsafe
+/// binder types.
 ///
 /// Implemented for:
 /// * `&T`, `&mut T` for all `T`,
diff --git a/tests/ui/unsafe-fields/unsafe-fields.rs b/tests/ui/unsafe-fields/unsafe-fields.rs
index 637471582d7..cb86479bb20 100644
--- a/tests/ui/unsafe-fields/unsafe-fields.rs
+++ b/tests/ui/unsafe-fields/unsafe-fields.rs
@@ -17,7 +17,7 @@ fn f(a: A) {
 }
 
 struct WithInvalidUnsafeField {
-    unsafe unsafe_noncopy_field: Vec<u32>, //~ ERROR
+    unsafe unsafe_noncopy_field: Vec<u32>,
 }
 
 struct WithManuallyDropUnsafeField {
diff --git a/tests/ui/unsafe-fields/unsafe-fields.stderr b/tests/ui/unsafe-fields/unsafe-fields.stderr
index a1c5d2b44cd..d0e2dc16a13 100644
--- a/tests/ui/unsafe-fields/unsafe-fields.stderr
+++ b/tests/ui/unsafe-fields/unsafe-fields.stderr
@@ -1,15 +1,3 @@
-error[E0740]: field must implement `Copy` or be wrapped in `ManuallyDrop<...>` to be unsafe
-  --> $DIR/unsafe-fields.rs:20:5
-   |
-LL |     unsafe unsafe_noncopy_field: Vec<u32>,
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: unsafe fields must not have drop side-effects, which is currently enforced via either `Copy` or `ManuallyDrop<...>`
-help: wrap the field type in `ManuallyDrop<...>`
-   |
-LL |     unsafe unsafe_noncopy_field: std::mem::ManuallyDrop<Vec<u32>>,
-   |                                  +++++++++++++++++++++++        +
-
 error[E0133]: use of unsafe field is unsafe and requires unsafe block
   --> $DIR/unsafe-fields.rs:15:30
    |
@@ -69,7 +57,6 @@ LL |         &raw const self.unsafe_field
    |
    = note: unsafe fields may carry library invariants
 
-error: aborting due to 8 previous errors
+error: aborting due to 7 previous errors
 
-Some errors have detailed explanations: E0133, E0740.
-For more information about an error, try `rustc --explain E0133`.
+For more information about this error, try `rustc --explain E0133`.