about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorMaybe Lapkin <waffle.lapkin@gmail.com>2024-06-23 19:55:17 +0200
committerMaybe Lapkin <waffle.lapkin@gmail.com>2024-07-04 17:57:29 +0200
commit340d69be12ffc03709340ec2d342db32406cd25f (patch)
treed63bc4b06c1e16d26458338af9b626cb4bd1a669 /compiler
parentc7435571ad725791494c32a48be1cd2c027af30b (diff)
downloadrust-340d69be12ffc03709340ec2d342db32406cd25f.tar.gz
rust-340d69be12ffc03709340ec2d342db32406cd25f.zip
Align the changes to the lang decision
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs37
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl5
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs46
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs8
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs54
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs8
6 files changed, 144 insertions, 14 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 2600c1fbff7..f98ac80a4c9 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -2323,27 +2323,48 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                                 let src_tail = tcx.struct_tail_without_normalization(src.ty);
                                 let dst_tail = tcx.struct_tail_without_normalization(dst.ty);
 
-                                if let ty::Dynamic(..) = src_tail.kind()
+                                if let ty::Dynamic(src_tty, ..) = src_tail.kind()
                                     && let ty::Dynamic(dst_tty, ..) = dst_tail.kind()
+                                    && src_tty.principal().is_some()
                                     && dst_tty.principal().is_some()
                                 {
-                                    // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`.
-                                    let src_tail =
-                                        erase_single_trait_object_lifetime(tcx, src_tail);
-                                    let dst_tail =
-                                        erase_single_trait_object_lifetime(tcx, dst_tail);
+                                    // Erase trait object lifetimes, to allow casts like `*mut dyn FnOnce()` -> `*mut dyn FnOnce() + 'static`
+                                    // and remove auto traits.
+                                    let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                                        tcx.mk_poly_existential_predicates(
+                                            &src_tty.without_auto_traits().collect::<Vec<_>>(),
+                                        ),
+                                        tcx.lifetimes.re_erased,
+                                        ty::Dyn,
+                                    ));
+                                    let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                                        tcx.mk_poly_existential_predicates(
+                                            &dst_tty.without_auto_traits().collect::<Vec<_>>(),
+                                        ),
+                                        tcx.lifetimes.re_erased,
+                                        ty::Dyn,
+                                    ));
+
+                                    // FIXME:
+                                    // this currently does nothing, but once we make `ptr_cast_add_auto_to_object`
+                                    // into a hard error, we can remove the above removal of auto traits and only
+                                    // keep this.
+                                    let src_obj = erase_single_trait_object_lifetime(tcx, src_obj);
+                                    let dst_obj = erase_single_trait_object_lifetime(tcx, dst_obj);
 
                                     let trait_ref = ty::TraitRef::new(
                                         tcx,
                                         tcx.require_lang_item(LangItem::Unsize, Some(span)),
-                                        [src_tail, dst_tail],
+                                        [src_obj, dst_obj],
                                     );
 
+                                    debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
+
                                     self.prove_trait_ref(
                                         trait_ref,
                                         location.to_locations(),
                                         ConstraintCategory::Cast {
-                                            unsize_to: Some(tcx.fold_regions(dst_tail, |r, _| {
+                                            unsize_to: Some(tcx.fold_regions(dst_obj, |r, _| {
                                                 if let ty::ReVar(_) = r.kind() {
                                                     tcx.lifetimes.re_erased
                                                 } else {
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index d6f3f4d640b..f05da5ad48a 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -123,6 +123,11 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
 hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
 hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
 
+hir_typeck_ptr_cast_add_auto_to_object = adding an auto {$traits_len ->
+    [1] trait {$traits}
+    *[other] traits {$traits}
+} to a trait object in a pointer cast may cause UB later on
+
 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
 hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 1809f5e4b04..c2f481e6c2f 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -32,6 +32,8 @@ use super::FnCtxt;
 
 use crate::errors;
 use crate::type_error_struct;
+use itertools::Itertools;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
 use rustc_hir::{self as hir, ExprKind, LangItem};
 use rustc_infer::traits::Obligation;
@@ -827,15 +829,16 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             // trait object -> trait object? need to do additional checks
             (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
                 match (src_tty.principal(), dst_tty.principal()) {
-                    // A<dyn Trait + Auto> -> B<dyn Trait' + Auto'>. need to make sure
-                    // - traits are the same
+                    // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
+                    // - `Src` and `Dst` traits are the same
                     // - traits have the same generic arguments
-                    // - Auto' is a subset of Auto
+                    // - `SrcAuto` is a superset of `DstAuto`
                     (Some(src_principal), Some(dst_principal)) => {
                         let tcx = fcx.tcx;
 
                         // Check that the traits are actually the same
                         // (this is required as the `Unsize` check below would allow upcasting, etc)
+                        // N.B.: this is only correct as long as we don't support `trait A<T>: A<()>`.
                         if src_principal.def_id() != dst_principal.def_id() {
                             return Err(CastError::DifferingKinds);
                         }
@@ -845,18 +848,24 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                         // contain wrappers, which we do not care about.
                         //
                         // e.g. we want to allow `dyn T -> (dyn T,)`, etc.
+                        //
+                        // We also need to skip auto traits to emit an FCW and not an error.
                         let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
-                            src_tty,
+                            tcx.mk_poly_existential_predicates(
+                                &src_tty.without_auto_traits().collect::<Vec<_>>(),
+                            ),
                             tcx.lifetimes.re_erased,
                             ty::Dyn,
                         ));
                         let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
-                            dst_tty,
+                            tcx.mk_poly_existential_predicates(
+                                &dst_tty.without_auto_traits().collect::<Vec<_>>(),
+                            ),
                             tcx.lifetimes.re_erased,
                             ty::Dyn,
                         ));
 
-                        // `dyn Src: Unsize<dyn Dst>`
+                        // `dyn Src: Unsize<dyn Dst>`, this checks for matching generics
                         let cause = fcx.misc(self.span);
                         let obligation = Obligation::new(
                             tcx,
@@ -871,6 +880,31 @@ impl<'a, 'tcx> CastCheck<'tcx> {
 
                         fcx.register_predicate(obligation);
 
+                        // Check that `SrcAuto` is a superset of `DstAuto`.
+                        // Emit an FCW otherwise.
+                        let src_auto = src_tty.auto_traits().collect::<FxHashSet<_>>();
+                        let added = dst_tty
+                            .auto_traits()
+                            .filter(|trait_did| !src_auto.contains(trait_did))
+                            .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: added
+                                        .into_iter()
+                                        .map(|trait_did| {
+                                            format!("`{}`", tcx.def_path_str(trait_did))
+                                        })
+                                        .join(", "),
+                                },
+                            )
+                        }
+
                         // FIXME: ideally we'd maybe add a flag here, so that borrowck knows that
                         //        it needs to borrowck this ptr cast. this is made annoying by the
                         //        fact that `thir` does not have `CastKind` and mir restores it
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 98add86252c..6c10047cfd4 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -253,6 +253,14 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
     pub sugg: LossyProvenanceInt2PtrSuggestion,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
+//#[help]
+pub struct PtrCastAddAutoToObject {
+    pub traits_len: usize,
+    pub traits: String,
+}
+
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
 pub struct LossyProvenanceInt2PtrSuggestion {
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 2ade6964ca8..6af6f7c473e 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -80,6 +80,7 @@ declare_lint_pass! {
         PRIVATE_BOUNDS,
         PRIVATE_INTERFACES,
         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+        PTR_CAST_ADD_AUTO_TO_OBJECT,
         PUB_USE_OF_PRIVATE_EXTERN_CRATE,
         REDUNDANT_LIFETIMES,
         REFINING_IMPL_TRAIT_INTERNAL,
@@ -4938,6 +4939,59 @@ 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,
+        // FIXME: actually write an issue with an explanation
+        reference: "issue #125289 <https://github.com/rust-lang/rust/issues/125289>",
+    };
+}
+
+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.
     ///
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index e9b37503bb3..3c327eb5b1d 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -341,6 +341,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
             _ => None,
         })
     }
+
+    pub fn without_auto_traits(
+        &self,
+    ) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ {
+        self.iter().filter(|predicate| {
+            !matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_))
+        })
+    }
 }
 
 pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;