about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-12-08 13:32:19 +0000
committerbors <bors@rust-lang.org>2024-12-08 13:32:19 +0000
commitf33a8c6426074b7ce8d08740e9805fdca96ee150 (patch)
treeb69e06be18e5280eb3c586a77ef64005f2ff1eb8 /compiler
parentf415c07494b98e4559e4b13a9c5f867b0e6b2444 (diff)
parentd4b5345248dd5ba96eb20bc5241921533692fc15 (diff)
downloadrust-f33a8c6426074b7ce8d08740e9805fdca96ee150.tar.gz
rust-f33a8c6426074b7ce8d08740e9805fdca96ee150.zip
Auto merge of #134033 - matthiaskrgr:rollup-69fswyh, r=matthiaskrgr
Rollup of 7 pull requests

Successful merges:

 - #131669 (lint: change help for pointers to dyn types in FFI)
 - #133104 (crashes: add test for #131451)
 - #133767 (Add more info on type/trait mismatches for different crate versions)
 - #133861 (Add allocate_bytes and refactor allocate_str in InterpCx for raw byte…)
 - #133976 (Removed Unnecessary Spaces From RELEASES.md)
 - #133987 (Define acronym for thread local storage)
 - #133992 (Actually walk into lifetimes and attrs in `EarlyContextAndPass`)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_const_eval/src/interpret/place.rs45
-rw-r--r--compiler/rustc_lint/messages.ftl13
-rw-r--r--compiler/rustc_lint/src/early.rs2
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs1
-rw-r--r--compiler/rustc_lint/src/lints.rs45
-rw-r--r--compiler/rustc_lint/src/types.rs372
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs134
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs4
9 files changed, 516 insertions, 101 deletions
diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 2beec544fad..f54a932e1b6 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -1018,29 +1018,48 @@ where
         self.allocate_dyn(layout, kind, MemPlaceMeta::None)
     }
 
-    /// Returns a wide MPlace of type `str` to a new 1-aligned allocation.
-    /// Immutable strings are deduplicated and stored in global memory.
-    pub fn allocate_str(
+    /// Allocates a sequence of bytes in the interpreter's memory.
+    /// For immutable allocations, uses deduplication to reuse existing memory.
+    /// For mutable allocations, creates a new unique allocation.
+    pub fn allocate_bytes(
         &mut self,
-        str: &str,
+        bytes: &[u8],
+        align: Align,
         kind: MemoryKind<M::MemoryKind>,
         mutbl: Mutability,
-    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
-        let tcx = self.tcx.tcx;
-
+    ) -> InterpResult<'tcx, Pointer<M::Provenance>> {
         // Use cache for immutable strings.
-        let ptr = if mutbl.is_not() {
+        if mutbl.is_not() {
             // Use dedup'd allocation function.
             let salt = M::get_global_alloc_salt(self, None);
-            let id = tcx.allocate_bytes_dedup(str.as_bytes(), salt);
+            let id = self.tcx.allocate_bytes_dedup(bytes, salt);
 
             // Turn untagged "global" pointers (obtained via `tcx`) into the machine pointer to the allocation.
-            M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))?
+            M::adjust_alloc_root_pointer(&self, Pointer::from(id), Some(kind))
         } else {
-            self.allocate_bytes_ptr(str.as_bytes(), Align::ONE, kind, mutbl)?
-        };
-        let meta = Scalar::from_target_usize(u64::try_from(str.len()).unwrap(), self);
+            // Allocate new memory for mutable data.
+            self.allocate_bytes_ptr(bytes, align, kind, mutbl)
+        }
+    }
+
+    /// Allocates a string in the interpreter's memory with metadata for length.
+    /// Uses `allocate_bytes` internally but adds string-specific metadata handling.
+    pub fn allocate_str(
+        &mut self,
+        str: &str,
+        kind: MemoryKind<M::MemoryKind>,
+        mutbl: Mutability,
+    ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> {
+        let bytes = str.as_bytes();
+        let ptr = self.allocate_bytes(bytes, Align::ONE, kind, mutbl)?;
+
+        // Create length metadata for the string.
+        let meta = Scalar::from_target_usize(u64::try_from(bytes.len()).unwrap(), self);
+
+        // Get layout for Rust's str type.
         let layout = self.layout_of(self.tcx.types.str_).unwrap();
+
+        // Combine pointer and metadata into a wide pointer.
         interp_ok(self.ptr_with_meta_to_mplace(
             ptr.into(),
             MemPlaceMeta::Meta(meta),
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 49e6b763590..422629cd11d 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -359,7 +359,6 @@ lint_improper_ctypes_128bit = 128-bit integers don't currently have a known stab
 lint_improper_ctypes_array_help = consider passing a pointer to the array
 
 lint_improper_ctypes_array_reason = passing raw arrays by value is not FFI-safe
-lint_improper_ctypes_box = box cannot be represented as a single pointer
 
 lint_improper_ctypes_char_help = consider using `u32` or `libc::wchar_t` instead
 
@@ -377,7 +376,9 @@ lint_improper_ctypes_enum_repr_help =
 lint_improper_ctypes_enum_repr_reason = enum has no representation hint
 lint_improper_ctypes_fnptr_help = consider using an `extern fn(...) -> ...` function pointer instead
 
+lint_improper_ctypes_fnptr_indirect_reason = the function pointer to `{$ty}` is FFI-unsafe due to `{$inner_ty}`
 lint_improper_ctypes_fnptr_reason = this function pointer has Rust-specific calling convention
+
 lint_improper_ctypes_non_exhaustive = this enum is non-exhaustive
 lint_improper_ctypes_non_exhaustive_variant = this enum has non-exhaustive variants
 
@@ -388,7 +389,11 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
 lint_improper_ctypes_pat_help = consider using the base type instead
 
 lint_improper_ctypes_pat_reason = pattern types have no C equivalent
-lint_improper_ctypes_slice_help = consider using a raw pointer instead
+
+lint_improper_ctypes_sized_ptr_to_unsafe_type =
+    this reference (`{$ty}`) is ABI-compatible with a C pointer, but `{$inner_ty}` itself does not have a C layout
+
+lint_improper_ctypes_slice_help = consider using a raw pointer to the slice's first element (and a length) instead
 
 lint_improper_ctypes_slice_reason = slices have no C equivalent
 lint_improper_ctypes_str_help = consider using `*const u8` and a length instead
@@ -414,6 +419,10 @@ lint_improper_ctypes_union_layout_help = consider adding a `#[repr(C)]` or `#[re
 lint_improper_ctypes_union_layout_reason = this union has unspecified layout
 lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
 
+lint_improper_ctypes_unsized_box = this box for an unsized type contains metadata, which makes it incompatible with a C pointer
+lint_improper_ctypes_unsized_ptr = this pointer to an unsized type contains metadata, which makes it incompatible with a C pointer
+lint_improper_ctypes_unsized_ref = this reference to an unsized type contains metadata, which makes it incompatible with a C pointer
+
 lint_incomplete_include =
     include macro expected single expression in source
 
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 4f3184f1d7c..a68a2a7f983 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -245,6 +245,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
 
     fn visit_lifetime(&mut self, lt: &'a ast::Lifetime, _: ast_visit::LifetimeCtxt) {
         self.check_id(lt.id);
+        ast_visit::walk_lifetime(self, lt);
     }
 
     fn visit_path(&mut self, p: &'a ast::Path, id: ast::NodeId) {
@@ -259,6 +260,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
 
     fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
         lint_callback!(self, check_attribute, attr);
+        ast_visit::walk_attribute(self, attr);
     }
 
     fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
index 025fd452040..28368e1ab46 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -9,6 +9,7 @@ use crate::lints::{
 use crate::{EarlyContext, EarlyLintPass, LintContext};
 
 declare_lint! {
+    #[allow(text_direction_codepoint_in_literal)]
     /// The `text_direction_codepoint_in_literal` lint detects Unicode codepoints that change the
     /// visual representation of text on screen in a way that does not correspond to their on
     /// memory representation.
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 20822f23bf1..9fa263799eb 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1851,13 +1851,44 @@ pub(crate) struct UnpredictableFunctionPointerComparisonsSuggestion<'a> {
     pub right: Span,
 }
 
+pub(crate) struct ImproperCTypesLayer<'a> {
+    pub ty: Ty<'a>,
+    pub inner_ty: Option<Ty<'a>>,
+    pub note: DiagMessage,
+    pub span_note: Option<Span>,
+    pub help: Option<DiagMessage>,
+}
+
+impl<'a> Subdiagnostic for ImproperCTypesLayer<'a> {
+    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
+        self,
+        diag: &mut Diag<'_, G>,
+        f: &F,
+    ) {
+        diag.arg("ty", self.ty);
+        if let Some(ty) = self.inner_ty {
+            diag.arg("inner_ty", ty);
+        }
+
+        if let Some(help) = self.help {
+            let msg = f(diag, help.into());
+            diag.help(msg);
+        }
+
+        let msg = f(diag, self.note.into());
+        diag.note(msg);
+        if let Some(note) = self.span_note {
+            let msg = f(diag, fluent::lint_note.into());
+            diag.span_note(note, msg);
+        };
+    }
+}
+
 pub(crate) struct ImproperCTypes<'a> {
     pub ty: Ty<'a>,
     pub desc: &'a str,
     pub label: Span,
-    pub help: Option<DiagMessage>,
-    pub note: DiagMessage,
-    pub span_note: Option<Span>,
+    pub reasons: Vec<ImproperCTypesLayer<'a>>,
 }
 
 // Used because of the complexity of Option<DiagMessage>, DiagMessage, and Option<Span>
@@ -1867,12 +1898,8 @@ impl<'a> LintDiagnostic<'a, ()> for ImproperCTypes<'_> {
         diag.arg("ty", self.ty);
         diag.arg("desc", self.desc);
         diag.span_label(self.label, fluent::lint_label);
-        if let Some(help) = self.help {
-            diag.help(help);
-        }
-        diag.note(self.note);
-        if let Some(note) = self.span_note {
-            diag.span_note(note, fluent::lint_note);
+        for reason in self.reasons.into_iter() {
+            diag.subdiagnostic(reason);
         }
     }
 }
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 33650be056d..90d44371ab5 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -22,10 +22,10 @@ mod improper_ctypes;
 use crate::lints::{
     AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
     AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
-    AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
-    InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
-    UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
-    VariantSizeDifferencesDiag,
+    AtomicOrderingStore, ImproperCTypes, ImproperCTypesLayer, InvalidAtomicOrderingDiag,
+    InvalidNanComparisons, InvalidNanComparisonsSuggestion,
+    UnpredictableFunctionPointerComparisons, UnpredictableFunctionPointerComparisonsSuggestion,
+    UnusedComparisons, VariantSizeDifferencesDiag,
 };
 use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
 
@@ -727,7 +727,109 @@ struct CTypesVisitorState<'tcx> {
 enum FfiResult<'tcx> {
     FfiSafe,
     FfiPhantom(Ty<'tcx>),
-    FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
+    FfiUnsafe {
+        ty: Ty<'tcx>,
+        reason: DiagMessage,
+        help: Option<DiagMessage>,
+    },
+    FfiUnsafeWrapper {
+        ty: Ty<'tcx>,
+        reason: DiagMessage,
+        help: Option<DiagMessage>,
+        wrapped: Box<FfiResult<'tcx>>,
+    },
+}
+
+/// Determine if a type is sized or not, and wether it affects references/pointers/boxes to it
+#[derive(Clone, Copy)]
+enum TypeSizedness {
+    /// type of definite size (pointers are C-compatible)
+    Definite,
+    /// unsized type because it includes an opaque/foreign type (pointers are C-compatible)
+    UnsizedWithExternType,
+    /// unsized type for other reasons (slice, string, dyn Trait, closure, ...) (pointers are not C-compatible)
+    UnsizedWithMetadata,
+}
+
+/// Is this type unsized because it contains (or is) a foreign type?
+/// (Returns Err if the type happens to be sized after all)
+fn get_type_sizedness<'tcx, 'a>(cx: &'a LateContext<'tcx>, ty: Ty<'tcx>) -> TypeSizedness {
+    let tcx = cx.tcx;
+
+    if ty.is_sized(tcx, cx.typing_env()) {
+        TypeSizedness::Definite
+    } else {
+        match ty.kind() {
+            ty::Slice(_) => TypeSizedness::UnsizedWithMetadata,
+            ty::Str => TypeSizedness::UnsizedWithMetadata,
+            ty::Dynamic(..) => TypeSizedness::UnsizedWithMetadata,
+            ty::Foreign(..) => TypeSizedness::UnsizedWithExternType,
+            // While opaque types are checked for earlier, if a projection in a struct field
+            // normalizes to an opaque type, then it will reach this branch.
+            ty::Alias(ty::Opaque, ..) => todo!("We... don't know enough about this type yet?"),
+            ty::Adt(def, args) => {
+                // for now assume: boxes and phantoms don't mess with this
+                match def.adt_kind() {
+                    AdtKind::Union | AdtKind::Enum => {
+                        bug!("unions and enums are necessarily sized")
+                    }
+                    AdtKind::Struct => {
+                        if let Some(sym::cstring_type | sym::cstr_type) =
+                            tcx.get_diagnostic_name(def.did())
+                        {
+                            return TypeSizedness::UnsizedWithMetadata;
+                        }
+                        // FIXME: how do we deal with non-exhaustive unsized structs/unions?
+
+                        if def.non_enum_variant().fields.is_empty() {
+                            bug!("an empty struct is necessarily sized");
+                        }
+
+                        let variant = def.non_enum_variant();
+
+                        // only the last field may be unsized
+                        let n_fields = variant.fields.len();
+                        let last_field = &variant.fields[(n_fields - 1).into()];
+                        let field_ty = last_field.ty(cx.tcx, args);
+                        let field_ty = cx
+                            .tcx
+                            .try_normalize_erasing_regions(cx.typing_env(), field_ty)
+                            .unwrap_or(field_ty);
+                        match get_type_sizedness(cx, field_ty) {
+                            s @ (TypeSizedness::UnsizedWithMetadata
+                            | TypeSizedness::UnsizedWithExternType) => s,
+                            TypeSizedness::Definite => {
+                                bug!("failed to find the reason why struct `{:?}` is unsized", ty)
+                            }
+                        }
+                    }
+                }
+            }
+            ty::Tuple(tuple) => {
+                // only the last field may be unsized
+                let n_fields = tuple.len();
+                let field_ty: Ty<'tcx> = tuple[n_fields - 1];
+                //let field_ty = last_field.ty(cx.tcx, args);
+                let field_ty = cx
+                    .tcx
+                    .try_normalize_erasing_regions(cx.typing_env(), field_ty)
+                    .unwrap_or(field_ty);
+                match get_type_sizedness(cx, field_ty) {
+                    s @ (TypeSizedness::UnsizedWithMetadata
+                    | TypeSizedness::UnsizedWithExternType) => s,
+                    TypeSizedness::Definite => {
+                        bug!("failed to find the reason why tuple `{:?}` is unsized", ty)
+                    }
+                }
+            }
+            ty => {
+                bug!(
+                    "we shouldn't be trying to determine if this is unsized for a reason or another: `{:?}`",
+                    ty
+                )
+            }
+        }
+    }
 }
 
 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
@@ -764,7 +866,7 @@ fn ty_is_known_nonnull<'tcx>(
     match ty.kind() {
         ty::FnPtr(..) => true,
         ty::Ref(..) => true,
-        ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
+        ty::Adt(def, _) if def.is_box() => true,
         ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
             let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
 
@@ -933,12 +1035,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
     /// Check if the type is array and emit an unsafe type lint.
     fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
         if let ty::Array(..) = ty.kind() {
-            self.emit_ffi_unsafe_type_lint(
+            self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
                 ty,
-                sp,
-                fluent::lint_improper_ctypes_array_reason,
-                Some(fluent::lint_improper_ctypes_array_help),
-            );
+                note: fluent::lint_improper_ctypes_array_reason,
+                help: Some(fluent::lint_improper_ctypes_array_help),
+                inner_ty: None,
+                span_note: None,
+            }]);
             true
         } else {
             false
@@ -995,9 +1098,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
                 FfiSafe => false,
                 // `()` fields are FFI-safe!
-                FfiUnsafe { ty, .. } if ty.is_unit() => false,
+                FfiUnsafe { ty, .. } | FfiUnsafeWrapper { ty, .. } if ty.is_unit() => false,
                 FfiPhantom(..) => true,
-                r @ FfiUnsafe { .. } => return r,
+                r @ (FfiUnsafe { .. } | FfiUnsafeWrapper { .. }) => return r,
             }
         }
 
@@ -1031,16 +1134,47 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
 
         match *ty.kind() {
             ty::Adt(def, args) => {
-                if let Some(boxed) = ty.boxed_ty()
-                    && matches!(self.mode, CItemKind::Definition)
-                {
-                    if boxed.is_sized(tcx, self.cx.typing_env()) {
-                        return FfiSafe;
+                if let Some(inner_ty) = ty.boxed_ty() {
+                    if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
+                        get_type_sizedness(self.cx, inner_ty)
+                    {
+                        // discussion on declaration vs definition:
+                        // see the `ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _)` arm
+                        // of this `match *ty.kind()` block
+                        if matches!(self.mode, CItemKind::Definition) {
+                            return FfiSafe;
+                        } else {
+                            let inner_res = self.check_type_for_ffi(acc, inner_ty);
+                            return match inner_res {
+                                FfiUnsafe { .. } | FfiUnsafeWrapper { .. } => FfiUnsafeWrapper {
+                                    ty,
+                                    reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
+                                    wrapped: Box::new(inner_res),
+                                    help: None,
+                                },
+                                _ => inner_res,
+                            };
+                        }
                     } else {
+                        let help = match inner_ty.kind() {
+                            ty::Str => Some(fluent::lint_improper_ctypes_str_help),
+                            ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
+                            ty::Adt(def, _)
+                                if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
+                                    && matches!(
+                                        tcx.get_diagnostic_name(def.did()),
+                                        Some(sym::cstring_type | sym::cstr_type)
+                                    )
+                                    && !acc.base_ty.is_mutable_ptr() =>
+                            {
+                                Some(fluent::lint_improper_ctypes_cstr_help)
+                            }
+                            _ => None,
+                        };
                         return FfiUnsafe {
                             ty,
-                            reason: fluent::lint_improper_ctypes_box,
-                            help: None,
+                            reason: fluent::lint_improper_ctypes_unsized_box,
+                            help,
                         };
                     }
                 }
@@ -1196,15 +1330,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 help: Some(fluent::lint_improper_ctypes_tuple_help),
             },
 
-            ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
-                if {
-                    matches!(self.mode, CItemKind::Definition)
-                        && ty.is_sized(self.cx.tcx, self.cx.typing_env())
-                } =>
-            {
-                FfiSafe
-            }
-
             ty::RawPtr(ty, _)
                 if match ty.kind() {
                     ty::Tuple(tuple) => tuple.is_empty(),
@@ -1214,7 +1339,70 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 FfiSafe
             }
 
-            ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
+            ty::RawPtr(inner_ty, _) | ty::Ref(_, inner_ty, _) => {
+                if let TypeSizedness::UnsizedWithExternType | TypeSizedness::Definite =
+                    get_type_sizedness(self.cx, inner_ty)
+                {
+                    // there's a nuance on what this lint should do for
+                    // function definitions (`extern "C" fn fn_name(...) {...}`)
+                    // versus declarations (`unsafe extern "C" {fn fn_name(...);}`).
+                    // This is touched upon in https://github.com/rust-lang/rust/issues/66220
+                    // and https://github.com/rust-lang/rust/pull/72700
+                    //
+                    // The big question is: what does "ABI safety" mean? if you have something translated to a C pointer
+                    // (which has a stable layout) but points to FFI-unsafe type, is it safe?
+                    // On one hand, the function's ABI will match that of a similar C-declared function API,
+                    // on the other, dereferencing the pointer on the other side of the FFI boundary will be painful.
+                    // In this code, the opinion on is split between function declarations and function definitions,
+                    // with the idea that at least one side of the FFI boundary needs to treat the pointee as an opaque type.
+                    // For declarations, we see this as unsafe, but for definitions, we see this as safe.
+                    //
+                    // For extern function declarations, the actual definition of the function is written somewhere else,
+                    // meaning the declaration is free to express this opaqueness with an extern type (opaque caller-side) or a std::ffi::c_void (opaque callee-side)
+                    // For extern function definitions, however, in the case where the type is opaque caller-side, it is not opaque callee-side,
+                    // and having the full type information is necessary to compile the function.
+                    if matches!(self.mode, CItemKind::Definition) {
+                        return FfiSafe;
+                    } else if matches!(ty.kind(), ty::RawPtr(..))
+                        && matches!(inner_ty.kind(), ty::Tuple(tuple) if tuple.is_empty())
+                    {
+                        FfiSafe
+                    } else {
+                        let inner_res = self.check_type_for_ffi(acc, inner_ty);
+                        return match inner_res {
+                            FfiSafe => inner_res,
+                            _ => FfiUnsafeWrapper {
+                                ty,
+                                reason: fluent::lint_improper_ctypes_sized_ptr_to_unsafe_type,
+                                wrapped: Box::new(inner_res),
+                                help: None,
+                            },
+                        };
+                    }
+                } else {
+                    let help = match inner_ty.kind() {
+                        ty::Str => Some(fluent::lint_improper_ctypes_str_help),
+                        ty::Slice(_) => Some(fluent::lint_improper_ctypes_slice_help),
+                        ty::Adt(def, _)
+                            if matches!(def.adt_kind(), AdtKind::Struct | AdtKind::Union)
+                                && matches!(
+                                    tcx.get_diagnostic_name(def.did()),
+                                    Some(sym::cstring_type | sym::cstr_type)
+                                )
+                                && !acc.base_ty.is_mutable_ptr() =>
+                        {
+                            Some(fluent::lint_improper_ctypes_cstr_help)
+                        }
+                        _ => None,
+                    };
+                    let reason = match ty.kind() {
+                        ty::RawPtr(..) => fluent::lint_improper_ctypes_unsized_ptr,
+                        ty::Ref(..) => fluent::lint_improper_ctypes_unsized_ref,
+                        _ => unreachable!(),
+                    };
+                    FfiUnsafe { ty, reason, help }
+                }
+            }
 
             ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
 
@@ -1232,7 +1420,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                 for arg in sig.inputs() {
                     match self.check_type_for_ffi(acc, *arg) {
                         FfiSafe => {}
-                        r => return r,
+                        r => {
+                            return FfiUnsafeWrapper {
+                                ty,
+                                reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
+                                help: None,
+                                wrapped: Box::new(r),
+                            };
+                        }
                     }
                 }
 
@@ -1241,7 +1436,15 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
                     return FfiSafe;
                 }
 
-                self.check_type_for_ffi(acc, ret_ty)
+                match self.check_type_for_ffi(acc, ret_ty) {
+                    r @ (FfiSafe | FfiPhantom(_)) => r,
+                    r => FfiUnsafeWrapper {
+                        ty: ty.clone(),
+                        reason: fluent::lint_improper_ctypes_fnptr_indirect_reason,
+                        help: None,
+                        wrapped: Box::new(r),
+                    },
+                }
             }
 
             ty::Foreign(..) => FfiSafe,
@@ -1278,8 +1481,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         &mut self,
         ty: Ty<'tcx>,
         sp: Span,
-        note: DiagMessage,
-        help: Option<DiagMessage>,
+        mut reasons: Vec<ImproperCTypesLayer<'tcx>>,
     ) {
         let lint = match self.mode {
             CItemKind::Declaration => IMPROPER_CTYPES,
@@ -1289,21 +1491,17 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             CItemKind::Declaration => "block",
             CItemKind::Definition => "fn",
         };
-        let span_note = if let ty::Adt(def, _) = ty.kind()
-            && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
-        {
-            Some(sp)
-        } else {
-            None
-        };
-        self.cx.emit_span_lint(lint, sp, ImproperCTypes {
-            ty,
-            desc,
-            label: sp,
-            help,
-            note,
-            span_note,
-        });
+        for reason in reasons.iter_mut() {
+            reason.span_note = if let ty::Adt(def, _) = reason.ty.kind()
+                && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did())
+            {
+                Some(sp)
+            } else {
+                None
+            };
+        }
+
+        self.cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, reasons });
     }
 
     fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
@@ -1332,7 +1530,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             .visit_with(&mut ProhibitOpaqueTypes)
             .break_value()
         {
-            self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
+            self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
+                ty,
+                note: fluent::lint_improper_ctypes_opaque,
+                span_note: Some(sp),
+                help: None,
+                inner_ty: None,
+            }]);
             true
         } else {
             false
@@ -1371,15 +1575,71 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
         match self.check_type_for_ffi(&mut acc, ty) {
             FfiResult::FfiSafe => {}
             FfiResult::FfiPhantom(ty) => {
-                self.emit_ffi_unsafe_type_lint(
+                self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
                     ty,
-                    sp,
-                    fluent::lint_improper_ctypes_only_phantomdata,
-                    None,
-                );
+                    note: fluent::lint_improper_ctypes_only_phantomdata,
+                    span_note: None, // filled later
+                    help: None,
+                    inner_ty: None,
+                }]);
             }
             FfiResult::FfiUnsafe { ty, reason, help } => {
-                self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
+                self.emit_ffi_unsafe_type_lint(ty.clone(), sp, vec![ImproperCTypesLayer {
+                    ty,
+                    help,
+                    note: reason,
+                    span_note: None, // filled later
+                    inner_ty: None,
+                }]);
+            }
+            ffir @ FfiResult::FfiUnsafeWrapper { .. } => {
+                let mut ffiresult_recursor = ControlFlow::Continue(&ffir);
+                let mut cimproper_layers: Vec<ImproperCTypesLayer<'tcx>> = vec![];
+
+                // this whole while block converts the arbitrarily-deep
+                // FfiResult stack to an ImproperCTypesLayer Vec
+                while let ControlFlow::Continue(ref ffir_rec) = ffiresult_recursor {
+                    match ffir_rec {
+                        FfiResult::FfiPhantom(ty) => {
+                            if let Some(layer) = cimproper_layers.last_mut() {
+                                layer.inner_ty = Some(ty.clone());
+                            }
+                            cimproper_layers.push(ImproperCTypesLayer {
+                                ty: ty.clone(),
+                                inner_ty: None,
+                                help: None,
+                                note: fluent::lint_improper_ctypes_only_phantomdata,
+                                span_note: None, // filled later
+                            });
+                            ffiresult_recursor = ControlFlow::Break(());
+                        }
+                        FfiResult::FfiUnsafe { ty, reason, help }
+                        | FfiResult::FfiUnsafeWrapper { ty, reason, help, .. } => {
+                            if let Some(layer) = cimproper_layers.last_mut() {
+                                layer.inner_ty = Some(ty.clone());
+                            }
+                            cimproper_layers.push(ImproperCTypesLayer {
+                                ty: ty.clone(),
+                                inner_ty: None,
+                                help: help.clone(),
+                                note: reason.clone(),
+                                span_note: None, // filled later
+                            });
+
+                            if let FfiResult::FfiUnsafeWrapper { wrapped, .. } = ffir_rec {
+                                ffiresult_recursor = ControlFlow::Continue(wrapped.as_ref());
+                            } else {
+                                ffiresult_recursor = ControlFlow::Break(());
+                            }
+                        }
+                        FfiResult::FfiSafe => {
+                            bug!("malformed FfiResult stack: it should be unsafe all the way down")
+                        }
+                    };
+                }
+                // should always have at least one type
+                let last_ty = cimproper_layers.last().unwrap().ty.clone();
+                self.emit_ffi_unsafe_type_lint(last_ty, sp, cimproper_layers);
             }
         }
     }
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index d2b7ae620e2..54e927df3c4 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3929,6 +3929,7 @@ declare_lint! {
 }
 
 declare_lint! {
+    #[allow(text_direction_codepoint_in_literal)]
     /// The `text_direction_codepoint_in_comment` lint detects Unicode codepoints in comments that
     /// change the visual representation of text on screen in a way that does not correspond to
     /// their on memory representation.
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 8ba7969207e..f856a8d7abb 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -52,7 +52,9 @@ use std::{cmp, fmt, iter};
 
 use rustc_abi::ExternAbi;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_errors::{Applicability, Diag, DiagStyledString, IntoDiagArg, StringPart, pluralize};
+use rustc_errors::{
+    Applicability, Diag, DiagStyledString, IntoDiagArg, MultiSpan, StringPart, pluralize,
+};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
@@ -67,6 +69,7 @@ use rustc_middle::ty::{
     self, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable,
     TypeVisitableExt,
 };
+use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::{BytePos, DesugaringKind, Pos, Span, sym};
 use tracing::{debug, instrument};
 
@@ -211,7 +214,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     }
 
     /// Adds a note if the types come from similarly named crates
-    fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
+    fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) -> bool {
+        // FIXME(estebank): unify with `report_similar_impl_candidates`. The message is similar,
+        // even if the logic needed to detect the case is very different.
         use hir::def_id::CrateNum;
         use rustc_hir::definitions::DisambiguatedDefPathData;
         use ty::GenericArg;
@@ -285,7 +290,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
 
-        let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId| {
+        let report_path_match = |err: &mut Diag<'_>, did1: DefId, did2: DefId, ty: &str| -> bool {
             // Only report definitions from different crates. If both definitions
             // are from a local module we could have false positives, e.g.
             // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
@@ -297,24 +302,112 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
                 // We compare strings because DefPath can be different
                 // for imported and non-imported crates
+                let expected_str = self.tcx.def_path_str(did1);
+                let found_str = self.tcx.def_path_str(did2);
+                let Ok(expected_abs) = abs_path(did1) else { return false };
+                let Ok(found_abs) = abs_path(did2) else { return false };
                 let same_path = || -> Result<_, PrintError> {
-                    Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
-                        || abs_path(did1)? == abs_path(did2)?)
+                    Ok(expected_str == found_str || expected_abs == found_abs)
+                };
+                // We want to use as unique a type path as possible. If both types are "locally
+                // known" by the same name, we use the "absolute path" which uses the original
+                // crate name instead.
+                let (expected, found) = if expected_str == found_str {
+                    (expected_abs.join("::"), found_abs.join("::"))
+                } else {
+                    (expected_str.clone(), found_str.clone())
                 };
                 if same_path().unwrap_or(false) {
-                    let crate_name = self.tcx.crate_name(did1.krate);
-                    let msg = if did1.is_local() || did2.is_local() {
+                    // We've displayed "expected `a::b`, found `a::b`". We add context to
+                    // differentiate the different cases where that might happen.
+                    let expected_crate_name = self.tcx.crate_name(did1.krate);
+                    let found_crate_name = self.tcx.crate_name(did2.krate);
+                    let same_crate = expected_crate_name == found_crate_name;
+                    let expected_sp = self.tcx.def_span(did1);
+                    let found_sp = self.tcx.def_span(did2);
+
+                    let both_direct_dependencies = if !did1.is_local()
+                        && !did2.is_local()
+                        && let Some(data1) = self.tcx.extern_crate(did1.krate)
+                        && let Some(data2) = self.tcx.extern_crate(did2.krate)
+                        && data1.dependency_of == LOCAL_CRATE
+                        && data2.dependency_of == LOCAL_CRATE
+                    {
+                        // If both crates are directly depended on, we don't want to mention that
+                        // in the final message, as it is redundant wording.
+                        // We skip the case of semver trick, where one version of the local crate
+                        // depends on another version of itself by checking that both crates at play
+                        // are not the current one.
+                        true
+                    } else {
+                        false
+                    };
+
+                    let mut span: MultiSpan = vec![expected_sp, found_sp].into();
+                    span.push_span_label(
+                        self.tcx.def_span(did1),
+                        format!("this is the expected {ty} `{expected}`"),
+                    );
+                    span.push_span_label(
+                        self.tcx.def_span(did2),
+                        format!("this is the found {ty} `{found}`"),
+                    );
+                    for def_id in [did1, did2] {
+                        let crate_name = self.tcx.crate_name(def_id.krate);
+                        if !def_id.is_local()
+                            && let Some(data) = self.tcx.extern_crate(def_id.krate)
+                        {
+                            let descr = if same_crate {
+                                "one version of".to_string()
+                            } else {
+                                format!("one {ty} comes from")
+                            };
+                            let dependency = if both_direct_dependencies {
+                                if let rustc_session::cstore::ExternCrateSource::Extern(def_id) =
+                                    data.src
+                                    && let Some(name) = self.tcx.opt_item_name(def_id)
+                                {
+                                    format!(", which is renamed locally to `{name}`")
+                                } else {
+                                    String::new()
+                                }
+                            } else if data.dependency_of == LOCAL_CRATE {
+                                ", as a direct dependency of the current crate".to_string()
+                            } else {
+                                let dep = self.tcx.crate_name(data.dependency_of);
+                                format!(", as a dependency of crate `{dep}`")
+                            };
+                            span.push_span_label(
+                                data.span,
+                                format!("{descr} crate `{crate_name}` used here{dependency}"),
+                            );
+                        }
+                    }
+                    let msg = if (did1.is_local() || did2.is_local()) && same_crate {
+                        format!(
+                            "the crate `{expected_crate_name}` is compiled multiple times, \
+                             possibly with different configurations",
+                        )
+                    } else if same_crate {
                         format!(
-                            "the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
+                            "two different versions of crate `{expected_crate_name}` are being \
+                             used; two types coming from two different versions of the same crate \
+                             are different types even if they look the same",
                         )
                     } else {
                         format!(
-                            "perhaps two different versions of crate `{crate_name}` are being used?"
+                            "two types coming from two different crates are different types even \
+                             if they look the same",
                         )
                     };
-                    err.note(msg);
+                    err.span_note(span, msg);
+                    if same_crate {
+                        err.help("you can use `cargo tree` to explore your dependency tree");
+                    }
+                    return true;
                 }
             }
+            false
         };
         match terr {
             TypeError::Sorts(ref exp_found) => {
@@ -323,14 +416,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) =
                     (exp_found.expected.kind(), exp_found.found.kind())
                 {
-                    report_path_match(err, exp_adt.did(), found_adt.did());
+                    return report_path_match(err, exp_adt.did(), found_adt.did(), "type");
                 }
             }
             TypeError::Traits(ref exp_found) => {
-                report_path_match(err, exp_found.expected, exp_found.found);
+                return report_path_match(err, exp_found.expected, exp_found.found, "trait");
             }
             _ => (), // FIXME(#22750) handle traits and stuff
         }
+        false
     }
 
     fn note_error_origin(
@@ -1409,6 +1503,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             label_or_note(span, terr.to_string(self.tcx));
         }
 
+        if self.check_and_note_conflicting_crates(diag, terr) {
+            return;
+        }
+
         if let Some((expected, found, path)) = expected_found {
             let (expected_label, found_label, exp_found) = match exp_found {
                 Mismatch::Variable(ef) => (
@@ -1470,15 +1568,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         |prim: Ty<'tcx>, shadow: Ty<'tcx>, defid: DefId, diag: &mut Diag<'_>| {
                             let name = shadow.sort_string(self.tcx);
                             diag.note(format!(
-                                "{prim} and {name} have similar names, but are actually distinct types"
+                                "`{prim}` and {name} have similar names, but are actually distinct types"
+                            ));
+                            diag.note(format!(
+                                "one `{prim}` is a primitive defined by the language",
                             ));
-                            diag.note(format!("{prim} is a primitive defined by the language"));
                             let def_span = self.tcx.def_span(defid);
                             let msg = if defid.is_local() {
-                                format!("{name} is defined in the current crate")
+                                format!("the other {name} is defined in the current crate")
                             } else {
                                 let crate_name = self.tcx.crate_name(defid.krate);
-                                format!("{name} is defined in crate `{crate_name}`")
+                                format!("the other {name} is defined in crate `{crate_name}`")
                             };
                             diag.span_note(def_span, msg);
                         };
@@ -1666,8 +1766,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
 
-        self.check_and_note_conflicting_crates(diag, terr);
-
         self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
         if let Some(exp_found) = exp_found
             && let exp_found = TypeError::Sorts(exp_found)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 90b18253629..4fb02f60943 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1745,9 +1745,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     };
                     (
                         data.span,
-                        format!(
-                            "one version of crate `{crate_name}` is used here, as a {dependency}"
-                        ),
+                        format!("one version of crate `{crate_name}` used here, as a {dependency}"),
                     )
                 })
             {