about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorTravis Cross <tc@traviscross.com>2025-02-09 06:08:59 +0000
committerTravis Cross <tc@traviscross.com>2025-02-22 23:03:14 +0000
commitef337a6599145766fc73b2896c6ff3e0fef65b0b (patch)
treefe2503680c6ad232a5c8718ed2ad589ca63e7287 /compiler
parented49386d3aa3a445a9889707fd405df01723eced (diff)
downloadrust-ef337a6599145766fc73b2896c6ff3e0fef65b0b.tar.gz
rust-ef337a6599145766fc73b2896c6ff3e0fef65b0b.zip
Make `ptr_cast_add_auto_to_object` lint into hard error
In Rust 1.81, we added a FCW lint (including linting in dependencies)
against pointer casts that add an auto trait to dyn bounds.  This was
part of work making casts of pointers involving trait objects stricter
which was needed to restabilize trait upcasting.

We considered just making this a hard error at the time, but opted
against it due to breakage found by crater.  This breakage was mostly
due to the `anymap` crate which has been a persistent problem for us.

It's now a year later, and the fact that this is not yet a hard error
is giving us pause about stabilizing arbitrary self types and
`derive(CoercePointee)`.  So let's now make a hard error of this.
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0804.md41
-rw-r--r--compiler/rustc_error_codes/src/lib.rs1
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl9
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs28
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs9
-rw-r--r--compiler/rustc_lint/src/lib.rs5
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs53
7 files changed, 72 insertions, 74 deletions
diff --git a/compiler/rustc_error_codes/src/error_codes/E0804.md b/compiler/rustc_error_codes/src/error_codes/E0804.md
new file mode 100644
index 00000000000..9a6937c0b52
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0804.md
@@ -0,0 +1,41 @@
+An auto trait cannot be added to the bounds of a `dyn Trait` type via
+a pointer cast.
+
+Erroneous code example:
+
+```rust,edition2021,compile_fail,E0804
+let ptr: *const dyn core::any::Any = &();
+_ = ptr as *const (dyn core::any::Any + Send);
+```
+
+Adding an auto trait can make the vtable invalid, potentially causing
+UB in safe code afterwards. For example:
+
+```rust,edition2021,no_run
+use core::{mem::transmute, ptr::NonNull};
+
+trait Trait {
+    fn f(&self)
+    where
+        Self: Send;
+}
+
+impl Trait for NonNull<()> {
+    fn f(&self) {
+        unreachable!()
+    }
+}
+
+fn main() {
+    let unsend: &dyn Trait = &NonNull::dangling();
+    let bad: &(dyn Trait + Send) = unsafe { transmute(unsend) };
+    // This crashes, since the vtable for `NonNull as dyn Trait` does
+    // not have an entry for `Trait::f`.
+    bad.f();
+}
+```
+
+To fix this error, you can use `transmute` rather than pointer casts,
+but you must ensure that the vtable is valid for the pointer's type
+before calling a method on the trait object or allowing other code to
+do so.
diff --git a/compiler/rustc_error_codes/src/lib.rs b/compiler/rustc_error_codes/src/lib.rs
index 098ca42be2b..d53d5678832 100644
--- a/compiler/rustc_error_codes/src/lib.rs
+++ b/compiler/rustc_error_codes/src/lib.rs
@@ -547,6 +547,7 @@ E0800: 0800,
 E0801: 0801,
 E0802: 0802,
 E0803: 0803,
+E0804: 0804,
         );
     )
 }
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index a994b31aeb4..c9dea60ba9f 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -154,10 +154,13 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function
     .suggestion = cast the value to `{$cast_ty}`
     .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard
 
-hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len ->
-    [1] an auto trait {$traits}
+hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len ->
+    [1] auto trait {$traits}
     *[other] auto traits {$traits}
-} to a trait object in a pointer cast may cause UB later on
+} to dyn bound via pointer cast
+    .note = this could allow UB elsewhere
+    .help = use `transmute` if you're sure this is sound
+    .label = unsupported cast
 
 hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression
 hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index f5f6ada12c3..02a44d58a40 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -936,23 +936,19 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             .collect::<Vec<_>>();
 
                         if !added.is_empty() {
-                            tcx.emit_node_span_lint(
-                                lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT,
-                                self.expr.hir_id,
-                                self.span,
-                                errors::PtrCastAddAutoToObject {
-                                    traits_len: added.len(),
-                                    traits: {
-                                        let mut traits: Vec<_> = added
-                                            .into_iter()
-                                            .map(|trait_did| tcx.def_path_str(trait_did))
-                                            .collect();
-
-                                        traits.sort();
-                                        traits.into()
-                                    },
+                            tcx.dcx().emit_err(errors::PtrCastAddAutoToObject {
+                                span: self.span,
+                                traits_len: added.len(),
+                                traits: {
+                                    let mut traits: Vec<_> = added
+                                        .into_iter()
+                                        .map(|trait_did| tcx.def_path_str(trait_did))
+                                        .collect();
+
+                                    traits.sort();
+                                    traits.into()
                                 },
-                            )
+                            });
                         }
 
                         Ok(CastKind::PtrPtrCast)
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 1bf8aa4f78d..f2ca7d22203 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -373,9 +373,14 @@ pub(crate) struct LossyProvenanceInt2Ptr<'tcx> {
     pub sugg: LossyProvenanceInt2PtrSuggestion,
 }
 
-#[derive(LintDiagnostic)]
-#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
+#[derive(Diagnostic)]
+#[diag(hir_typeck_ptr_cast_add_auto_to_object, code = E0804)]
+#[note]
+#[help]
 pub(crate) struct PtrCastAddAutoToObject {
+    #[primary_span]
+    #[label]
+    pub span: Span,
     pub traits_len: usize,
     pub traits: DiagSymbolList<String>,
 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index c8de5e87753..1dc487d3511 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -601,6 +601,11 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, \
          see <https://github.com/rust-lang/rust/issues/73333> for more information",
     );
+    store.register_removed(
+        "ptr_cast_add_auto_to_object",
+        "converted into hard error, see issue #127323 \
+         <https://github.com/rust-lang/rust/issues/127323> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 10bf4ec77ed..42e288976f9 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -80,7 +80,6 @@ declare_lint_pass! {
         PRIVATE_BOUNDS,
         PRIVATE_INTERFACES,
         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
-        PTR_CAST_ADD_AUTO_TO_OBJECT,
         PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS,
         PUB_USE_OF_PRIVATE_EXTERN_CRATE,
         REDUNDANT_IMPORTS,
@@ -4828,58 +4827,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait
-    /// objects, which add auto traits.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,edition2021,compile_fail
-    /// let ptr: *const dyn core::any::Any = &();
-    /// _ = ptr as *const dyn core::any::Any + Send;
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// Adding an auto trait can make the vtable invalid, potentially causing
-    /// UB in safe code afterwards. For example:
-    ///
-    /// ```ignore (causes a warning)
-    /// #![feature(arbitrary_self_types)]
-    ///
-    /// trait Trait {
-    ///     fn f(self: *const Self)
-    ///     where
-    ///         Self: Send;
-    /// }
-    ///
-    /// impl Trait for *const () {
-    ///     fn f(self: *const Self) {
-    ///         unreachable!()
-    ///     }
-    /// }
-    ///
-    /// fn main() {
-    ///     let unsend: *const () = &();
-    ///     let unsend: *const dyn Trait = &unsend;
-    ///     let send_bad: *const (dyn Trait + Send) = unsend as _;
-    ///     send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f`
-    /// }
-    /// ```
-    ///
-    /// Generally you must ensure that vtable is right for the pointer's type,
-    /// before passing the pointer to safe code.
-    pub PTR_CAST_ADD_AUTO_TO_OBJECT,
-    Warn,
-    "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
-        reference: "issue #127323 <https://github.com/rust-lang/rust/issues/127323>",
-    };
-}
-
-declare_lint! {
     /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
     /// above their definition, which may happen in key-value attributes.
     ///