about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-09-03 06:00:10 +0000
committerbors <bors@rust-lang.org>2024-09-03 06:00:10 +0000
commitc01d5e03f2350fe032db7dbdc932a290c2fccb34 (patch)
tree9aa5c9e92f095d8866e21808a4164b4ec66c0235 /src
parent6faf40992e166e0086e59e4353abcfed47e0143c (diff)
parent111c690426ac42e3f8c2db011fc74dfbb91d81d3 (diff)
downloadrust-c01d5e03f2350fe032db7dbdc932a290c2fccb34.tar.gz
rust-c01d5e03f2350fe032db7dbdc932a290c2fccb34.zip
Auto merge of #17984 - ShoyuVanilla:cast, r=Veykril
feat: Implement cast typecheck and diagnostics

Fixes  #17897 and fixes #16564
Mainly adopted from https://github.com/rust-lang/rust/blob/100fde5246bf56f22fb5cc85374dd841296fce0e/compiler/rustc_hir_typeck/src/cast.rs
Diffstat (limited to 'src')
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs32
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs417
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs79
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs1
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs30
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs1007
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs19
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs1
25 files changed, 1614 insertions, 93 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
index a70710e565c..ba54451e594 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs
@@ -14,6 +14,7 @@ use triomphe::Arc;
 use crate::{
     builtin_type::{BuiltinInt, BuiltinUint},
     db::DefDatabase,
+    hir::Expr,
     item_tree::{
         AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId,
     },
@@ -317,6 +318,27 @@ impl EnumData {
             _ => IntegerType::Pointer(true),
         }
     }
+
+    // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448)
+    pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool {
+        self.variants.iter().all(|(v, _)| {
+            // The condition check order is slightly modified from rustc
+            // to improve performance by early returning with relatively fast checks
+            let variant = &db.enum_variant_data(*v).variant_data;
+            if !variant.fields().is_empty() {
+                return false;
+            }
+            // The outer if condition is whether this variant has const ctor or not
+            if !matches!(variant.kind(), StructKind::Unit) {
+                let body = db.body((*v).into());
+                // A variant with explicit discriminant
+                if body.exprs[body.body_expr] != Expr::Missing {
+                    return false;
+                }
+            }
+            true
+        })
+    }
 }
 
 impl EnumVariantData {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
index c4cbaaa3017..203e21302cd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs
@@ -186,7 +186,13 @@ fn floating_point() {
 
 #[test]
 fn casts() {
-    check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12);
+    check_number(
+        r#"
+    //- minicore: sized
+    const GOAL: usize = 12 as *const i32 as usize
+        "#,
+        12,
+    );
     check_number(
         r#"
     //- minicore: coerce_unsized, index, slice
@@ -204,7 +210,7 @@ fn casts() {
         r#"
     //- minicore: coerce_unsized, index, slice
     const GOAL: i16 = {
-        let a = &mut 5;
+        let a = &mut 5_i16;
         let z = a as *mut _;
         unsafe { *z }
     };
@@ -244,7 +250,13 @@ fn casts() {
         "#,
         4,
     );
-    check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12);
+    check_number(
+        r#"
+    //- minicore: sized
+    const GOAL: i32 = -12i8 as i32
+        "#,
+        -12,
+    );
 }
 
 #[test]
@@ -1911,6 +1923,7 @@ fn function_pointer() {
     );
     check_number(
         r#"
+    //- minicore: sized
     fn add2(x: u8) -> u8 {
         x + 2
     }
@@ -2422,6 +2435,7 @@ fn statics() {
 fn extern_weak_statics() {
     check_number(
         r#"
+    //- minicore: sized
     extern "C" {
         #[linkage = "extern_weak"]
         static __dso_handle: *mut u8;
@@ -2716,6 +2730,7 @@ fn const_trait_assoc() {
     );
     check_number(
         r#"
+    //- minicore: sized
     struct S<T>(*mut T);
 
     trait MySized: Sized {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 6f294494c01..c5706172b20 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -311,6 +311,7 @@ fn saturating() {
 fn allocator() {
     check_number(
         r#"
+        //- minicore: sized
         extern "Rust" {
             #[rustc_allocator]
             fn __rust_alloc(size: usize, align: usize) -> *mut u8;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index 062ea278151..0e68ab4bfec 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -13,7 +13,7 @@
 //! to certain types. To record this, we use the union-find implementation from
 //! the `ena` crate, which is extracted from rustc.
 
-mod cast;
+pub(crate) mod cast;
 pub(crate) mod closure;
 mod coerce;
 mod expr;
@@ -76,7 +76,7 @@ pub use coerce::could_coerce;
 #[allow(unreachable_pub)]
 pub use unify::{could_unify, could_unify_deeply};
 
-use cast::CastCheck;
+use cast::{CastCheck, CastError};
 pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy};
 
 /// The entry point of type inference.
@@ -254,6 +254,16 @@ pub enum InferenceDiagnostic {
         expr: ExprId,
         expected: Ty,
     },
+    CastToUnsized {
+        expr: ExprId,
+        cast_ty: Ty,
+    },
+    InvalidCast {
+        expr: ExprId,
+        error: CastError,
+        expr_ty: Ty,
+        cast_ty: Ty,
+    },
 }
 
 /// A mismatch between an expected and an inferred type.
@@ -456,6 +466,7 @@ pub struct InferenceResult {
     pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
     // FIXME: remove this field
     pub mutated_bindings_in_closure: FxHashSet<BindingId>,
+    pub coercion_casts: FxHashSet<ExprId>,
 }
 
 impl InferenceResult {
@@ -666,7 +677,7 @@ impl<'a> InferenceContext<'a> {
         let InferenceContext {
             mut table,
             mut result,
-            deferred_cast_checks,
+            mut deferred_cast_checks,
             tuple_field_accesses_rev,
             ..
         } = self;
@@ -695,6 +706,7 @@ impl<'a> InferenceContext<'a> {
             closure_info: _,
             mutated_bindings_in_closure: _,
             tuple_field_access_types: _,
+            coercion_casts,
         } = &mut result;
 
         table.fallback_if_possible();
@@ -702,8 +714,18 @@ impl<'a> InferenceContext<'a> {
         // Comment from rustc:
         // Even though coercion casts provide type hints, we check casts after fallback for
         // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
-        for cast in deferred_cast_checks {
-            cast.check(&mut table);
+        let mut apply_adjustments = |expr, adj| {
+            expr_adjustments.insert(expr, adj);
+        };
+        let mut set_coercion_cast = |expr| {
+            coercion_casts.insert(expr);
+        };
+        for cast in deferred_cast_checks.iter_mut() {
+            if let Err(diag) =
+                cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast)
+            {
+                diagnostics.push(diag);
+            }
         }
 
         // FIXME resolve obligations as well (use Guidance if necessary)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index 060b5f36f29..ae914044368 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -1,47 +1,412 @@
 //! Type cast logic. Basically coercion + additional casts.
 
-use crate::{infer::unify::InferenceTable, Interner, Ty, TyExt, TyKind};
+use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy};
+use hir_def::{hir::ExprId, AdtId};
+use stdx::never;
+
+use crate::{
+    infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner,
+    PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause,
+};
+
+#[derive(Debug)]
+pub(crate) enum Int {
+    I,
+    U(UintTy),
+    Bool,
+    Char,
+    CEnum,
+    InferenceVar,
+}
+
+#[derive(Debug)]
+pub(crate) enum CastTy {
+    Int(Int),
+    Float,
+    FnPtr,
+    Ptr(Ty, Mutability),
+    // `DynStar` is Not supported yet in r-a
+}
+
+impl CastTy {
+    pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option<Self> {
+        match t.kind(Interner) {
+            TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)),
+            TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)),
+            TyKind::Scalar(Scalar::Int(_)) => Some(Self::Int(Int::I)),
+            TyKind::Scalar(Scalar::Uint(it)) => Some(Self::Int(Int::U(*it))),
+            TyKind::InferenceVar(_, TyVariableKind::Integer) => Some(Self::Int(Int::InferenceVar)),
+            TyKind::InferenceVar(_, TyVariableKind::Float) => Some(Self::Float),
+            TyKind::Scalar(Scalar::Float(_)) => Some(Self::Float),
+            TyKind::Adt(..) => {
+                let (AdtId::EnumId(id), _) = t.as_adt()? else {
+                    return None;
+                };
+                let enum_data = table.db.enum_data(id);
+                if enum_data.is_payload_free(table.db.upcast()) {
+                    Some(Self::Int(Int::CEnum))
+                } else {
+                    None
+                }
+            }
+            TyKind::Raw(m, ty) => Some(Self::Ptr(table.resolve_ty_shallow(ty), *m)),
+            TyKind::Function(_) => Some(Self::FnPtr),
+            _ => None,
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum CastError {
+    Unknown,
+    CastToBool,
+    CastToChar,
+    DifferingKinds,
+    SizedUnsizedCast,
+    IllegalCast,
+    IntToFatCast,
+    NeedDeref,
+    NeedViaPtr,
+    NeedViaThinPtr,
+    NeedViaInt,
+    NonScalar,
+    UnknownCastPtrKind,
+    UnknownExprPtrKind,
+}
+
+impl CastError {
+    fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic {
+        InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty }
+    }
+}
 
 #[derive(Clone, Debug)]
 pub(super) struct CastCheck {
+    expr: ExprId,
+    source_expr: ExprId,
     expr_ty: Ty,
     cast_ty: Ty,
 }
 
 impl CastCheck {
-    pub(super) fn new(expr_ty: Ty, cast_ty: Ty) -> Self {
-        Self { expr_ty, cast_ty }
+    pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self {
+        Self { expr, source_expr, expr_ty, cast_ty }
+    }
+
+    pub(super) fn check<F, G>(
+        &mut self,
+        table: &mut InferenceTable<'_>,
+        apply_adjustments: &mut F,
+        set_coercion_cast: &mut G,
+    ) -> Result<(), InferenceDiagnostic>
+    where
+        F: FnMut(ExprId, Vec<Adjustment>),
+        G: FnMut(ExprId),
+    {
+        table.resolve_obligations_as_possible();
+        self.expr_ty = table.resolve_ty_shallow(&self.expr_ty);
+        self.cast_ty = table.resolve_ty_shallow(&self.cast_ty);
+
+        if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() {
+            return Ok(());
+        }
+
+        if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER)
+            && !table.is_sized(&self.cast_ty)
+        {
+            return Err(InferenceDiagnostic::CastToUnsized {
+                expr: self.expr,
+                cast_ty: self.cast_ty.clone(),
+            });
+        }
+
+        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) {
+            apply_adjustments(self.source_expr, adj);
+            set_coercion_cast(self.source_expr);
+            return Ok(());
+        }
+
+        self.do_check(table, apply_adjustments)
+            .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone()))
+    }
+
+    fn do_check<F>(
+        &self,
+        table: &mut InferenceTable<'_>,
+        apply_adjustments: &mut F,
+    ) -> Result<(), CastError>
+    where
+        F: FnMut(ExprId, Vec<Adjustment>),
+    {
+        let (t_from, t_cast) =
+            match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) {
+                (Some(t_from), Some(t_cast)) => (t_from, t_cast),
+                (None, Some(t_cast)) => match self.expr_ty.kind(Interner) {
+                    TyKind::FnDef(..) => {
+                        let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
+                        let sig = table.normalize_associated_types_in(sig);
+                        let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
+                        if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) {
+                            apply_adjustments(self.source_expr, adj);
+                        } else {
+                            return Err(CastError::IllegalCast);
+                        }
+
+                        (CastTy::FnPtr, t_cast)
+                    }
+                    TyKind::Ref(mutbl, _, inner_ty) => {
+                        let inner_ty = table.resolve_ty_shallow(inner_ty);
+                        return match t_cast {
+                            CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) {
+                                TyKind::Scalar(
+                                    Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_),
+                                )
+                                | TyKind::InferenceVar(
+                                    _,
+                                    TyVariableKind::Integer | TyVariableKind::Float,
+                                ) => Err(CastError::NeedDeref),
+
+                                _ => Err(CastError::NeedViaPtr),
+                            },
+                            // array-ptr-cast
+                            CastTy::Ptr(t, m) => {
+                                let t = table.resolve_ty_shallow(&t);
+                                if !table.is_sized(&t) {
+                                    return Err(CastError::IllegalCast);
+                                }
+                                self.check_ref_cast(
+                                    table,
+                                    &inner_ty,
+                                    *mutbl,
+                                    &t,
+                                    m,
+                                    apply_adjustments,
+                                )
+                            }
+                            _ => Err(CastError::NonScalar),
+                        };
+                    }
+                    _ => return Err(CastError::NonScalar),
+                },
+                _ => return Err(CastError::NonScalar),
+            };
+
+        // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym
+
+        match (t_from, t_cast) {
+            (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar),
+            (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool),
+            (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()),
+            (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar),
+            (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => {
+                Err(CastError::NeedViaInt)
+            }
+            (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..))
+            | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast),
+            (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => {
+                self.check_ptr_ptr_cast(table, &src, &dst)
+            }
+            (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src),
+            (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst),
+            (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst),
+            (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()),
+            (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()),
+            (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()),
+            (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
+        }
     }
 
-    pub(super) fn check(self, table: &mut InferenceTable<'_>) {
-        // FIXME: This function currently only implements the bits that influence the type
-        // inference. We should return the adjustments on success and report diagnostics on error.
-        let expr_ty = table.resolve_ty_shallow(&self.expr_ty);
-        let cast_ty = table.resolve_ty_shallow(&self.cast_ty);
+    fn check_ref_cast<F>(
+        &self,
+        table: &mut InferenceTable<'_>,
+        t_expr: &Ty,
+        m_expr: Mutability,
+        t_cast: &Ty,
+        m_cast: Mutability,
+        apply_adjustments: &mut F,
+    ) -> Result<(), CastError>
+    where
+        F: FnMut(ExprId, Vec<Adjustment>),
+    {
+        // Mutability order is opposite to rustc. `Mut < Not`
+        if m_expr <= m_cast {
+            if let TyKind::Array(ety, _) = t_expr.kind(Interner) {
+                // Coerce to a raw pointer so that we generate RawPtr in MIR.
+                let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner);
+                if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) {
+                    apply_adjustments(self.source_expr, adj);
+                } else {
+                    never!(
+                        "could not cast from reference to array to pointer to array ({:?} to {:?})",
+                        self.expr_ty,
+                        array_ptr_type
+                    );
+                }
 
-        if table.coerce(&expr_ty, &cast_ty).is_ok() {
-            return;
+                // This is a less strict condition than rustc's `demand_eqtype`,
+                // but false negative is better than false positive
+                if table.coerce(ety, t_cast).is_ok() {
+                    return Ok(());
+                }
+            }
         }
 
-        if check_ref_to_ptr_cast(expr_ty, cast_ty, table) {
-            // Note that this type of cast is actually split into a coercion to a
-            // pointer type and a cast:
-            // &[T; N] -> *[T; N] -> *T
+        Err(CastError::IllegalCast)
+    }
+
+    fn check_ptr_ptr_cast(
+        &self,
+        table: &mut InferenceTable<'_>,
+        src: &Ty,
+        dst: &Ty,
+    ) -> Result<(), CastError> {
+        let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?;
+        let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?;
+
+        match (src_kind, dst_kind) {
+            (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
+            (_, None) => Err(CastError::UnknownCastPtrKind),
+            (_, Some(PointerKind::Thin)) => Ok(()),
+            (None, _) => Err(CastError::UnknownExprPtrKind),
+            (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
+            (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
+                let principal = |tty: &Binders<QuantifiedWhereClauses>| {
+                    tty.skip_binders().as_slice(Interner).first().and_then(|pred| {
+                        if let WhereClause::Implemented(tr) = pred.skip_binders() {
+                            Some(tr.trait_id)
+                        } else {
+                            None
+                        }
+                    })
+                };
+                match (principal(&src_tty), principal(&dst_tty)) {
+                    (Some(src_principal), Some(dst_principal)) => {
+                        if src_principal == dst_principal {
+                            return Ok(());
+                        }
+                        let src_principal =
+                            table.db.trait_datum(table.trait_env.krate, src_principal);
+                        let dst_principal =
+                            table.db.trait_datum(table.trait_env.krate, dst_principal);
+                        if src_principal.is_auto_trait() && dst_principal.is_auto_trait() {
+                            Ok(())
+                        } else {
+                            Err(CastError::DifferingKinds)
+                        }
+                    }
+                    _ => Err(CastError::Unknown),
+                }
+            }
+            (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()),
+            (_, _) => Err(CastError::DifferingKinds),
+        }
+    }
+
+    fn check_ptr_addr_cast(
+        &self,
+        table: &mut InferenceTable<'_>,
+        expr_ty: &Ty,
+    ) -> Result<(), CastError> {
+        match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? {
+            None => Err(CastError::UnknownExprPtrKind),
+            Some(PointerKind::Error) => Ok(()),
+            Some(PointerKind::Thin) => Ok(()),
+            _ => Err(CastError::NeedViaThinPtr),
+        }
+    }
+
+    fn check_addr_ptr_cast(
+        &self,
+        table: &mut InferenceTable<'_>,
+        cast_ty: &Ty,
+    ) -> Result<(), CastError> {
+        match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? {
+            None => Err(CastError::UnknownCastPtrKind),
+            Some(PointerKind::Error) => Ok(()),
+            Some(PointerKind::Thin) => Ok(()),
+            Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast),
+            Some(PointerKind::Length) => Err(CastError::IntToFatCast),
+            Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast),
         }
+    }
 
-        // FIXME: Check other kinds of non-coercion casts and report error if any?
+    fn check_fptr_ptr_cast(
+        &self,
+        table: &mut InferenceTable<'_>,
+        cast_ty: &Ty,
+    ) -> Result<(), CastError> {
+        match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? {
+            None => Err(CastError::UnknownCastPtrKind),
+            Some(PointerKind::Error) => Ok(()),
+            Some(PointerKind::Thin) => Ok(()),
+            _ => Err(CastError::IllegalCast),
+        }
     }
 }
 
-fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool {
-    let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else {
-        return false;
-    };
-    let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else {
-        return false;
-    };
-    let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else {
-        return false;
-    };
-    table.coerce(expr_elt_ty, cast_inner_ty).is_ok()
+#[derive(PartialEq, Eq)]
+enum PointerKind {
+    // thin pointer
+    Thin,
+    // trait object
+    VTable(Binders<QuantifiedWhereClauses>),
+    // slice
+    Length,
+    OfAlias,
+    OfParam(PlaceholderIndex),
+    Error,
+}
+
+fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
+    let ty = table.resolve_ty_shallow(ty);
+
+    if table.is_sized(&ty) {
+        return Ok(Some(PointerKind::Thin));
+    }
+
+    match ty.kind(Interner) {
+        TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)),
+        TyKind::Dyn(DynTy { bounds, .. }) => Ok(Some(PointerKind::VTable(bounds.clone()))),
+        TyKind::Adt(chalk_ir::AdtId(id), subst) => {
+            let AdtId::StructId(id) = *id else {
+                never!("`{:?}` should be sized but is not?", ty);
+                return Err(());
+            };
+
+            let struct_data = table.db.struct_data(id);
+            if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {
+                let last_field_ty =
+                    table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst);
+                pointer_kind(&last_field_ty, table)
+            } else {
+                Ok(Some(PointerKind::Thin))
+            }
+        }
+        TyKind::Tuple(_, subst) => {
+            match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) {
+                None => Ok(Some(PointerKind::Thin)),
+                Some(ty) => pointer_kind(ty, table),
+            }
+        }
+        TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)),
+        TyKind::Alias(_) | TyKind::AssociatedType(..) | TyKind::OpaqueType(..) => {
+            Ok(Some(PointerKind::OfAlias))
+        }
+        TyKind::Error => Ok(Some(PointerKind::Error)),
+        TyKind::Placeholder(idx) => Ok(Some(PointerKind::OfParam(*idx))),
+        TyKind::BoundVar(_) | TyKind::InferenceVar(..) => Ok(None),
+        TyKind::Scalar(_)
+        | TyKind::Array(..)
+        | TyKind::CoroutineWitness(..)
+        | TyKind::Raw(..)
+        | TyKind::Ref(..)
+        | TyKind::FnDef(..)
+        | TyKind::Function(_)
+        | TyKind::Closure(..)
+        | TyKind::Coroutine(..)
+        | TyKind::Never => {
+            never!("`{:?}` should be sized but is not?", ty);
+            Err(())
+        }
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index 89d92ea9af0..b1c793a1e38 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -610,7 +610,12 @@ impl InferenceContext<'_> {
             Expr::Cast { expr, type_ref } => {
                 let cast_ty = self.make_ty(type_ref);
                 let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone()));
-                self.deferred_cast_checks.push(CastCheck::new(expr_ty, cast_ty.clone()));
+                self.deferred_cast_checks.push(CastCheck::new(
+                    tgt_expr,
+                    *expr,
+                    expr_ty,
+                    cast_ty.clone(),
+                ));
                 cast_ty
             }
             Expr::Ref { expr, rawness, mutability } => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index c0f5ddddcbe..99ad6789809 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -9,6 +9,7 @@ use chalk_ir::{
 use chalk_solve::infer::ParameterEnaVariableExt;
 use either::Either;
 use ena::unify::UnifyKey;
+use hir_def::{lang_item::LangItem, AdtId};
 use hir_expand::name::Name;
 use intern::sym;
 use rustc_hash::FxHashMap;
@@ -21,7 +22,7 @@ use crate::{
     to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue,
     DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment,
     InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar,
-    Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind,
+    Solution, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind,
     WhereClause,
 };
 
@@ -898,6 +899,33 @@ impl<'a> InferenceTable<'a> {
             _ => c,
         }
     }
+
+    /// Check if given type is `Sized` or not
+    pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
+        if let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
+            let struct_data = self.db.struct_data(id);
+            if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {
+                let last_field_ty =
+                    self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst);
+                // Structs can have DST as its last field and such cases are not handled
+                // as unsized by the chalk, so we do this manually
+                return self.is_sized(&last_field_ty);
+            }
+        }
+        let Some(sized) = self
+            .db
+            .lang_item(self.trait_env.krate, LangItem::Sized)
+            .and_then(|sized| sized.as_trait())
+        else {
+            return false;
+        };
+        let sized_pred = WhereClause::Implemented(TraitRef {
+            trait_id: to_chalk_trait_id(sized),
+            substitution: Substitution::from1(Interner, ty.clone()),
+        });
+        let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner);
+        matches!(self.try_obligation(goal), Some(Solution::Unique(_)))
+    }
 }
 
 impl fmt::Debug for InferenceTable<'_> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index a316de2b48d..79ee9e97871 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -83,6 +83,7 @@ pub use autoderef::autoderef;
 pub use builder::{ParamKind, TyBuilder};
 pub use chalk_ext::*;
 pub use infer::{
+    cast::CastError,
     closure::{CaptureKind, CapturedItem},
     could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode,
     InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index 22d4da0e755..8cd6ae12326 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -837,7 +837,9 @@ pub enum CastKind {
     PointerFromExposedAddress,
     /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are
     /// translated into `&raw mut/const *r`, i.e., they are not actually casts.
-    Pointer(PointerCast),
+    PtrToPtr,
+    /// Pointer related casts that are done by coercions.
+    PointerCoercion(PointerCast),
     /// Cast into a dyn* object.
     DynStar,
     IntToInt,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 1bb0c188685..89b2ecb1a63 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -1475,7 +1475,7 @@ impl Evaluator<'_> {
                 }
             }
             Rvalue::Cast(kind, operand, target_ty) => match kind {
-                CastKind::Pointer(cast) => match cast {
+                CastKind::PointerCoercion(cast) => match cast {
                     PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => {
                         let current_ty = self.operand_ty(operand, locals)?;
                         if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) =
@@ -1506,6 +1506,7 @@ impl Evaluator<'_> {
                 },
                 CastKind::DynStar => not_supported!("dyn star cast"),
                 CastKind::IntToInt
+                | CastKind::PtrToPtr
                 | CastKind::PointerExposeAddress
                 | CastKind::PointerFromExposedAddress => {
                     let current_ty = self.operand_ty(operand, locals)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
index 6825bc4862a..595a78da10f 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs
@@ -399,7 +399,7 @@ extern "C" {
     fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32;
 }
 
-fn my_cmp(x: &[u8], y: &[u8]) -> i32 {
+fn my_cmp(x: &[u8; 3], y: &[u8; 3]) -> i32 {
     memcmp(x as *const u8, y as *const u8, x.len())
 }
 
@@ -779,6 +779,7 @@ fn main() {
 fn posix_getenv() {
     check_pass(
         r#"
+//- minicore: sized
 //- /main.rs env:foo=bar
 
 type c_char = u8;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 9e235504519..9cee494bbe7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -31,7 +31,7 @@ use crate::{
     display::HirDisplay,
     error_lifetime,
     generics::generics,
-    infer::{CaptureKind, CapturedItem, TypeMismatch},
+    infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch},
     inhabitedness::is_ty_uninhabited_from,
     layout::LayoutError,
     mapping::ToChalk,
@@ -362,7 +362,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
                         current,
                         place,
                         Rvalue::Cast(
-                            CastKind::Pointer(*cast),
+                            CastKind::PointerCoercion(*cast),
                             Operand::Copy(p),
                             last.target.clone(),
                         ),
@@ -898,14 +898,26 @@ impl<'ctx> MirLowerCtx<'ctx> {
                 let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else {
                     return Ok(None);
                 };
-                let source_ty = self.infer[*expr].clone();
-                let target_ty = self.infer[expr_id].clone();
-                self.push_assignment(
-                    current,
-                    place,
-                    Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty),
-                    expr_id.into(),
-                );
+                // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178)
+                // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313)
+                let rvalue = if self.infer.coercion_casts.contains(expr) {
+                    Rvalue::Use(it)
+                } else {
+                    let source_ty = self.infer[*expr].clone();
+                    let target_ty = self.infer[expr_id].clone();
+                    let cast_kind = if source_ty.as_reference().is_some() {
+                        CastKind::PointerCoercion(PointerCast::ArrayToPointer)
+                    } else {
+                        let mut table = InferenceTable::new(
+                            self.db,
+                            self.db.trait_environment_for_body(self.owner),
+                        );
+                        cast_kind(&mut table, &source_ty, &target_ty)?
+                    };
+
+                    Rvalue::Cast(cast_kind, it, target_ty)
+                };
+                self.push_assignment(current, place, rvalue, expr_id.into());
                 Ok(Some(current))
             }
             Expr::Ref { expr, rawness: _, mutability } => {
@@ -2005,40 +2017,21 @@ impl<'ctx> MirLowerCtx<'ctx> {
     }
 }
 
-fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
-    Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) {
-        (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer),
-        (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) {
-            (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat,
-            (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt,
-            (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat,
-            (_, _) => CastKind::IntToInt,
-        },
-        (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress,
-        (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress,
-        (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => {
-            CastKind::Pointer(if a == b {
-                PointerCast::MutToConstPointer
-            } else if matches!(b.kind(Interner), TyKind::Slice(_))
-                && matches!(a.kind(Interner), TyKind::Array(_, _))
-                || matches!(b.kind(Interner), TyKind::Dyn(_))
-            {
-                PointerCast::Unsize
-            } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) {
-                PointerCast::ArrayToPointer
-            } else {
-                // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like
-                // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata
-                // will be touched) but there is no specific variant
-                // for it in `PointerCast` so we use `MutToConstPointer`
-                PointerCast::MutToConstPointer
-            })
-        }
-        // Enum to int casts
-        (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => {
-            CastKind::IntToInt
+fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result<CastKind> {
+    let from = CastTy::from_ty(table, source_ty);
+    let cast = CastTy::from_ty(table, target_ty);
+    Ok(match (from, cast) {
+        (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => {
+            CastKind::PointerExposeAddress
         }
-        (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"),
+        (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress,
+        (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt,
+        (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr,
+        (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt,
+        (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat,
+        (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat,
+        (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr,
+        _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"),
     })
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
index 610fc9b6b45..74acf23b75a 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs
@@ -1286,6 +1286,7 @@ fn main() {
 fn method_on_dyn_impl() {
     check_types(
         r#"
+//- minicore: coerce_unsized
 trait Foo {}
 
 impl Foo for u32 {}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 98f30f0f6f7..4df10025f80 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -1907,6 +1907,7 @@ fn dont_unify_on_casts() {
     // #15246
     check_types(
         r#"
+//- minicore: sized
 fn unify(_: [bool; 1]) {}
 fn casted(_: *const bool) {}
 fn default<T>() -> T { loop {} }
@@ -1926,6 +1927,7 @@ fn test() {
 fn rustc_test_issue_52437() {
     check_types(
         r#"
+    //- minicore: sized
     fn main() {
         let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize]
           //^ [(); _]
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 1c6fa62e30c..37280f81b86 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3572,6 +3572,7 @@ fn f<T>(t: Ark<T>) {
 fn ref_to_array_to_ptr_cast() {
     check_types(
         r#"
+//- minicore: sized
 fn default<T>() -> T { loop {} }
 fn foo() {
     let arr = [default()];
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index ffb972475f8..4d90df63b9b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -4,7 +4,9 @@
 //! This probably isn't the best way to do this -- ideally, diagnostics should
 //! be expressed in terms of hir types themselves.
 pub use hir_ty::diagnostics::{CaseType, IncorrectCase};
-use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDiagnostic};
+use hir_ty::{
+    db::HirDatabase, diagnostics::BodyValidationDiagnostic, CastError, InferenceDiagnostic,
+};
 
 use cfg::{CfgExpr, CfgOptions};
 use either::Either;
@@ -50,10 +52,12 @@ macro_rules! diagnostics {
 diagnostics![
     AwaitOutsideOfAsync,
     BreakOutsideOfLoop,
+    CastToUnsized,
     ExpectedFunction,
     InactiveCode,
     IncoherentImpl,
     IncorrectCase,
+    InvalidCast,
     InvalidDeriveTarget,
     MacroDefError,
     MacroError,
@@ -364,6 +368,20 @@ pub struct RemoveUnnecessaryElse {
     pub if_expr: InFile<AstPtr<ast::IfExpr>>,
 }
 
+#[derive(Debug)]
+pub struct CastToUnsized {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub cast_ty: Type,
+}
+
+#[derive(Debug)]
+pub struct InvalidCast {
+    pub expr: InFile<AstPtr<ast::Expr>>,
+    pub error: CastError,
+    pub expr_ty: Type,
+    pub cast_ty: Type,
+}
+
 impl AnyDiagnostic {
     pub(crate) fn body_validation_diagnostic(
         db: &dyn HirDatabase,
@@ -620,6 +638,16 @@ impl AnyDiagnostic {
                 };
                 MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into()
             }
+            InferenceDiagnostic::CastToUnsized { expr, cast_ty } => {
+                let expr = expr_syntax(*expr)?;
+                CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into()
+            }
+            InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => {
+                let expr = expr_syntax(*expr)?;
+                let expr_ty = Type::new(db, def, expr_ty.clone());
+                let cast_ty = Type::new(db, def, cast_ty.clone());
+                InvalidCast { expr, error: *error, expr_ty, cast_ty }.into()
+            }
         })
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 6328a3c3d0d..81d6466c2f3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -147,7 +147,7 @@ pub use {
         layout::LayoutError,
         mir::{MirEvalError, MirLowerError},
         object_safety::{MethodViolationCode, ObjectSafetyViolation},
-        FnAbi, PointerCast, Safety,
+        CastError, FnAbi, PointerCast, Safety,
     },
     // FIXME: Properly encapsulate mir
     hir_ty::{mir, Interner as ChalkTyInterner},
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
index 5bd204dd573..906e9a9891a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs
@@ -1020,6 +1020,7 @@ fn main() {
         check_assist(
             inline_call,
             r#"
+//- minicore: sized
 fn foo(x: *const u32) -> u32 {
     x as u32
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
index 6a1f7f26c92..b9fc075ae83 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs
@@ -333,7 +333,8 @@ fn foo() {
         check_assist(
             inline_local_variable,
             r"
-fn bar(a: usize): usize { a }
+//- minicore: sized
+fn bar(a: usize) -> usize { a }
 fn foo() {
     let a$0 = bar(1) as u64;
     a + 1;
@@ -347,7 +348,7 @@ fn foo() {
     bar(a);
 }",
             r"
-fn bar(a: usize): usize { a }
+fn bar(a: usize) -> usize { a }
 fn foo() {
     (bar(1) as u64) + 1;
     if (bar(1) as u64) > 10 {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
new file mode 100644
index 00000000000..2f6f033f25c
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -0,0 +1,1007 @@
+use hir::{CastError, ClosureStyle, HirDisplay};
+
+use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext};
+
+macro_rules! format_ty {
+    ($ctx:expr, $fmt:literal, $($arg:expr),* $(,)?) => {{
+        std::format!(
+            $fmt,
+            $(
+                $arg
+                    .display($ctx.sema.db, $ctx.edition)
+                    .with_closure_style(ClosureStyle::ClosureWithId)
+            ),*
+        )
+    }}
+}
+
+// Diagnostic: invalid-cast
+//
+// This diagnostic is triggered if the code contains an illegal cast
+pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -> Diagnostic {
+    let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
+    let (code, message) = match d.error {
+        CastError::CastToBool => (
+            DiagnosticCode::RustcHardError("E0054"),
+            format_ty!(ctx, "cannot cast `{}` as `bool`", d.expr_ty),
+        ),
+        CastError::CastToChar => (
+            DiagnosticCode::RustcHardError("E0604"),
+            format_ty!(ctx, "only `u8` can be cast as `char`, not {}", d.expr_ty),
+        ),
+        CastError::DifferingKinds => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(
+                ctx,
+                "casting `{}` as `{}` is invalid: vtable kinds may not match",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::SizedUnsizedCast => (
+            DiagnosticCode::RustcHardError("E0607"),
+            format_ty!(
+                ctx,
+                "cannot cast thin pointer `{}` to fat pointer `{}`",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::Unknown | CastError::IllegalCast => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty),
+        ),
+        CastError::IntToFatCast => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty),
+        ),
+        CastError::NeedDeref => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(
+                ctx,
+                "casting `{}` as `{}` is invalid: needs defererence or removal of unneeded borrow",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::NeedViaPtr => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(
+                ctx,
+                "casting `{}` as `{}` is invalid: needs casting through a raw pointer first",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::NeedViaThinPtr => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(
+                ctx,
+                "casting `{}` as `{}` is invalid: needs casting through a thin pointer first",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::NeedViaInt => (
+            DiagnosticCode::RustcHardError("E0606"),
+            format_ty!(
+                ctx,
+                "casting `{}` as `{}` is invalid: needs casting through an integer first",
+                d.expr_ty,
+                d.cast_ty
+            ),
+        ),
+        CastError::NonScalar => (
+            DiagnosticCode::RustcHardError("E0605"),
+            format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty),
+        ),
+        CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => (
+            DiagnosticCode::RustcHardError("E0641"),
+            "cannot cast to a pointer of an unknown kind".to_owned(),
+        ),
+    };
+    Diagnostic::new(code, message, display_range)
+}
+
+// Diagnostic: cast-to-unsized
+//
+// This diagnostic is triggered when casting to an unsized type
+pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsized) -> Diagnostic {
+    let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into()));
+    Diagnostic::new(
+        DiagnosticCode::RustcHardError("E0620"),
+        format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty),
+        display_range,
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_diagnostics, check_diagnostics_with_disabled};
+
+    #[test]
+    fn cast_as_bool() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let u = 5 as bool;
+          //^^^^^^^^^ error: cannot cast `i32` as `bool`
+
+    let t = (1 + 2) as bool;
+          //^^^^^^^^^^^^^^^ error: cannot cast `i32` as `bool`
+
+    let _ = 5_u32 as bool;
+          //^^^^^^^^^^^^^ error: cannot cast `u32` as `bool`
+
+    let _ = 64.0_f64 as bool;
+          //^^^^^^^^^^^^^^^^ error: cannot cast `f64` as `bool`
+
+    enum IntEnum {
+        Zero,
+        One,
+        Two
+    }
+    let _ = IntEnum::One as bool;
+          //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `IntEnum` as `bool`
+
+    fn uwu(_: u8) -> i32 {
+        5
+    }
+
+    unsafe fn owo() {}
+
+    let _ = uwu as bool;
+          //^^^^^^^^^^^ error: cannot cast `fn uwu(u8) -> i32` as `bool`
+    let _ = owo as bool;
+          //^^^^^^^^^^^ error: cannot cast `unsafe fn owo()` as `bool`
+
+    let _ = uwu as fn(u8) -> i32 as bool;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot cast `fn(u8) -> i32` as `bool`
+    let _ = 'x' as bool;
+          //^^^^^^^^^^^ error: cannot cast `char` as `bool`
+
+    let ptr = 1 as *const ();
+
+    let _ = ptr as bool;
+          //^^^^^^^^^^^ error: cannot cast `*const ()` as `bool`
+    let v = "hello" as bool;
+          //^^^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn cast_pointee_projection() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+trait Tag<'a> {
+    type Type: ?Sized;
+}
+
+trait IntoRaw: for<'a> Tag<'a> {
+    fn into_raw(this: *const <Self as Tag<'_>>::Type) -> *mut <Self as Tag<'_>>::Type;
+}
+
+impl<T: for<'a> Tag<'a>> IntoRaw for T {
+    fn into_raw(this: *const <Self as Tag<'_>>::Type) -> *mut <Self as Tag<'_>>::Type {
+        this as *mut T::Type
+    }
+}
+
+fn main() {}
+"#,
+        );
+    }
+
+    #[test]
+    fn cast_region_to_int() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let x: isize = 3;
+    let _ = &x as *const isize as usize;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn cast_to_bare_fn() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn foo(_x: isize) { }
+
+fn main() {
+    let v: u64 = 5;
+    let x = foo as extern "C" fn() -> isize;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `fn() -> isize`
+    let y = v as extern "Rust" fn(isize) -> (isize, isize);
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)`
+    y(x());
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn cast_to_unit() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let _ = 0u32 as ();
+          //^^^^^^^^^^ error: non-primitive cast: `u32` as `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn cast_to_slice() {
+        check_diagnostics_with_disabled(
+            r#"
+//- minicore: sized
+fn as_bytes(_: &str) -> &[u8] {
+    loop {}
+}
+
+fn main() {
+    as_bytes("example") as [char];
+  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cast to unsized type: `[char]`
+
+    let arr: &[u8] = &[0, 2, 3];
+    arr as [char];
+  //^^^^^^^^^^^^^ error: cast to unsized type: `[char]`
+}
+"#,
+            &["E0308"],
+        );
+    }
+
+    #[test]
+    fn cast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn null_mut<T: ?Sized>() -> *mut T {
+    loop {}
+}
+
+pub fn main() {
+    let i: isize = 'Q' as isize;
+    let _u: u32 = i as u32;
+
+    // Test that `_` is correctly inferred.
+    let x = &"hello";
+    let mut y = x as *const _;
+    y = null_mut();
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn dyn_tail_need_normalization() {
+        check_diagnostics(
+            r#"
+//- minicore: dispatch_from_dyn
+trait Trait {
+    type Associated;
+}
+
+impl Trait for i32 {
+    type Associated = i64;
+}
+
+trait Generic<T> {}
+
+type TraitObject = dyn Generic<<i32 as Trait>::Associated>;
+
+struct Wrap(TraitObject);
+
+fn cast(x: *mut TraitObject) {
+    x as *mut Wrap;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn enum_to_numeric_cast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+pub enum UnitOnly {
+    Foo,
+    Bar,
+    Baz,
+}
+
+pub enum Fieldless {
+    Tuple(),
+    Struct{},
+    Unit,
+}
+
+pub enum NotUnitOnlyOrFieldless {
+    Foo,
+    Bar(u8),
+    Baz
+}
+
+fn main() {
+    let unit_only = UnitOnly::Foo;
+
+    let _ = unit_only as isize;
+    let _ = unit_only as i32;
+    let _ = unit_only as usize;
+    let _ = unit_only as u32;
+
+
+    let fieldless = Fieldless::Struct{};
+
+    let _ = fieldless as isize;
+    let _ = fieldless as i32;
+    let _ = fieldless as usize;
+    let _ = fieldless as u32;
+
+
+    let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo;
+
+    let _ = not_unit_only_or_fieldless as isize;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize`
+    let _ = not_unit_only_or_fieldless as i32;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32`
+    let _ = not_unit_only_or_fieldless as usize;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize`
+    let _ = not_unit_only_or_fieldless as u32;
+          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn fat_ptr_cast() {
+        check_diagnostics_with_disabled(
+            r#"
+//- minicore: sized
+trait Foo {
+    fn foo(&self) {} //~ WARN method `foo` is never used
+}
+
+struct Bar;
+
+impl Foo for Bar {}
+
+fn to_raw<T>(_: *mut T) -> *mut () {
+    loop {}
+}
+
+fn main() {
+    // Test we can turn a fat pointer to array back into a thin pointer.
+    let a: *const [i32] = &[1, 2, 3];
+    let b = a as *const [i32; 2];
+
+    // Test conversion to an address (usize).
+    let a: *const [i32; 3] = &[1, 2, 3];
+    let b: *const [i32] = a;
+
+    // And conversion to a void pointer/address for trait objects too.
+    let a: *mut dyn Foo = &mut Bar;
+    let b = a as *mut () as usize;
+    let c = a as *const () as usize;
+    let d = to_raw(a) as usize;
+}
+"#,
+            &["E0308"],
+        );
+
+        check_diagnostics_with_disabled(
+            r#"
+//- minicore: sized
+trait Trait {}
+
+struct Box<T: ?Sized>;
+
+impl<T: ?Sized> Box<T> {
+    fn new(_: T) -> Self {
+        loop {}
+    }
+}
+
+fn as_ptr(_: &[i32]) -> *const i32 {
+    loop {}
+}
+
+fn main() {
+    let a: &[i32] = &[1, 2, 3];
+    let b: Box<[i32]> = Box::new([1, 2, 3]);
+    let p = a as *const [i32];
+    let q = as_ptr(a);
+
+    a as usize;
+  //^^^^^^^^^^ error: casting `&[i32]` as `usize` is invalid: needs casting through a raw pointer first
+    a as isize;
+  //^^^^^^^^^^ error: casting `&[i32]` as `isize` is invalid: needs casting through a raw pointer first
+    a as i16;
+  //^^^^^^^^ error: casting `&[i32]` as `i16` is invalid: needs casting through a raw pointer first
+    a as u32;
+  //^^^^^^^^ error: casting `&[i32]` as `u32` is invalid: needs casting through a raw pointer first
+    b as usize;
+  //^^^^^^^^^^ error: non-primitive cast: `Box<[i32]>` as `usize`
+    p as usize;
+  //^^^^^^^^^^ error: casting `*const [i32]` as `usize` is invalid: needs casting through a thin pointer first
+    q as *const [i32];
+  //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]`
+
+    let t: *mut (dyn Trait + 'static) = 0 as *mut _;
+                                      //^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*mut _`
+    let mut fail: *const str = 0 as *const str;
+                             //^^^^^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*const str`
+    let mut fail2: *const str = 0isize as *const str;
+                              //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `isize` to a fat pointer `*const str`
+}
+
+fn foo<T: ?Sized>() {
+    let s = 0 as *const T;
+          //^^^^^^^^^^^^^ error: cannot cast `i32` to a fat pointer `*const T`
+}
+"#,
+            &["E0308", "unused_variables"],
+        );
+    }
+
+    #[test]
+    fn order_dependent_cast_inference() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let x = &"hello";
+    let mut y = 0 as *const _;
+              //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind
+    y = x as *const _;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ptr_to_ptr_different_regions() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+struct Foo<'a> { a: &'a () }
+
+fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> {
+    // This should pass because raw pointer casts can do anything they want.
+    v as *const Foo<'static>
+}
+
+trait Trait {}
+
+fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) {
+    ptr as _
+}
+
+fn main() {
+    let unit = ();
+    let foo = Foo { a: &unit };
+    let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ptr_to_trait_obj_add_auto() {
+        check_diagnostics(
+            r#"
+//- minicore: pointee
+trait Trait<'a> {}
+
+fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) {
+    x as _
+}
+
+// (to test diagnostic list formatting)
+fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) {
+    x as _
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ptr_to_trait_obj_add_super_auto() {
+        check_diagnostics(
+            r#"
+//- minicore: pointee
+trait Trait: Send {}
+impl Trait for () {}
+
+fn main() {
+    // This is OK: `Trait` has `Send` super trait.
+    &() as *const dyn Trait as *const (dyn Trait + Send);
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ptr_to_trait_obj_ok() {
+        check_diagnostics(
+            r#"
+//- minicore: pointee
+trait Trait<'a> {}
+
+fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> {
+    x as _
+}
+
+fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) {
+    x as _
+}
+
+fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) {
+    x as _
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn ptr_to_trait_obj_wrap_upcast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+trait Super {}
+trait Sub: Super {}
+
+struct Wrapper<T: ?Sized>(T);
+
+// This cast should not compile.
+// Upcasting can't work here, because we are also changing the type (`Wrapper`),
+// and reinterpreting would be confusing/surprising.
+// See <https://github.com/rust-lang/rust/pull/120248#discussion_r1487739518>
+fn cast(ptr: *const dyn Sub) -> *const Wrapper<dyn Super> {
+    ptr as _
+  //^^^^^^^^ error: casting `*const dyn Sub` as `*const Wrapper<dyn Super>` is invalid: vtable kinds may not match
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn supported_cast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+pub fn main() {
+    struct String;
+
+    let f = 1_usize as *const String;
+
+    let _ = f as isize;
+    let _ = f as usize;
+    let _ = f as i8;
+    let _ = f as i16;
+    let _ = f as i32;
+    let _ = f as i64;
+    let _ = f as u8;
+    let _ = f as u16;
+    let _ = f as u32;
+    let _ = f as u64;
+
+    let _ = 1 as isize;
+    let _ = 1 as usize;
+    let _ = 1 as *const String;
+    let _ = 1 as i8;
+    let _ = 1 as i16;
+    let _ = 1 as i32;
+    let _ = 1 as i64;
+    let _ = 1 as u8;
+    let _ = 1 as u16;
+    let _ = 1 as u32;
+    let _ = 1 as u64;
+    let _ = 1 as f32;
+    let _ = 1 as f64;
+
+    let _ = 1_usize as isize;
+    let _ = 1_usize as usize;
+    let _ = 1_usize as *const String;
+    let _ = 1_usize as i8;
+    let _ = 1_usize as i16;
+    let _ = 1_usize as i32;
+    let _ = 1_usize as i64;
+    let _ = 1_usize as u8;
+    let _ = 1_usize as u16;
+    let _ = 1_usize as u32;
+    let _ = 1_usize as u64;
+    let _ = 1_usize as f32;
+    let _ = 1_usize as f64;
+
+    let _ = 1i8 as isize;
+    let _ = 1i8 as usize;
+    let _ = 1i8 as *const String;
+    let _ = 1i8 as i8;
+    let _ = 1i8 as i16;
+    let _ = 1i8 as i32;
+    let _ = 1i8 as i64;
+    let _ = 1i8 as u8;
+    let _ = 1i8 as u16;
+    let _ = 1i8 as u32;
+    let _ = 1i8 as u64;
+    let _ = 1i8 as f32;
+    let _ = 1i8 as f64;
+
+    let _ = 1u8 as isize;
+    let _ = 1u8 as usize;
+    let _ = 1u8 as *const String;
+    let _ = 1u8 as i8;
+    let _ = 1u8 as i16;
+    let _ = 1u8 as i32;
+    let _ = 1u8 as i64;
+    let _ = 1u8 as u8;
+    let _ = 1u8 as u16;
+    let _ = 1u8 as u32;
+    let _ = 1u8 as u64;
+    let _ = 1u8 as f32;
+    let _ = 1u8 as f64;
+
+    let _ = 1i16 as isize;
+    let _ = 1i16 as usize;
+    let _ = 1i16 as *const String;
+    let _ = 1i16 as i8;
+    let _ = 1i16 as i16;
+    let _ = 1i16 as i32;
+    let _ = 1i16 as i64;
+    let _ = 1i16 as u8;
+    let _ = 1i16 as u16;
+    let _ = 1i16 as u32;
+    let _ = 1i16 as u64;
+    let _ = 1i16 as f32;
+    let _ = 1i16 as f64;
+
+    let _ = 1u16 as isize;
+    let _ = 1u16 as usize;
+    let _ = 1u16 as *const String;
+    let _ = 1u16 as i8;
+    let _ = 1u16 as i16;
+    let _ = 1u16 as i32;
+    let _ = 1u16 as i64;
+    let _ = 1u16 as u8;
+    let _ = 1u16 as u16;
+    let _ = 1u16 as u32;
+    let _ = 1u16 as u64;
+    let _ = 1u16 as f32;
+    let _ = 1u16 as f64;
+
+    let _ = 1i32 as isize;
+    let _ = 1i32 as usize;
+    let _ = 1i32 as *const String;
+    let _ = 1i32 as i8;
+    let _ = 1i32 as i16;
+    let _ = 1i32 as i32;
+    let _ = 1i32 as i64;
+    let _ = 1i32 as u8;
+    let _ = 1i32 as u16;
+    let _ = 1i32 as u32;
+    let _ = 1i32 as u64;
+    let _ = 1i32 as f32;
+    let _ = 1i32 as f64;
+
+    let _ = 1u32 as isize;
+    let _ = 1u32 as usize;
+    let _ = 1u32 as *const String;
+    let _ = 1u32 as i8;
+    let _ = 1u32 as i16;
+    let _ = 1u32 as i32;
+    let _ = 1u32 as i64;
+    let _ = 1u32 as u8;
+    let _ = 1u32 as u16;
+    let _ = 1u32 as u32;
+    let _ = 1u32 as u64;
+    let _ = 1u32 as f32;
+    let _ = 1u32 as f64;
+
+    let _ = 1i64 as isize;
+    let _ = 1i64 as usize;
+    let _ = 1i64 as *const String;
+    let _ = 1i64 as i8;
+    let _ = 1i64 as i16;
+    let _ = 1i64 as i32;
+    let _ = 1i64 as i64;
+    let _ = 1i64 as u8;
+    let _ = 1i64 as u16;
+    let _ = 1i64 as u32;
+    let _ = 1i64 as u64;
+    let _ = 1i64 as f32;
+    let _ = 1i64 as f64;
+
+    let _ = 1u64 as isize;
+    let _ = 1u64 as usize;
+    let _ = 1u64 as *const String;
+    let _ = 1u64 as i8;
+    let _ = 1u64 as i16;
+    let _ = 1u64 as i32;
+    let _ = 1u64 as i64;
+    let _ = 1u64 as u8;
+    let _ = 1u64 as u16;
+    let _ = 1u64 as u32;
+    let _ = 1u64 as u64;
+    let _ = 1u64 as f32;
+    let _ = 1u64 as f64;
+
+    let _ = 1u64 as isize;
+    let _ = 1u64 as usize;
+    let _ = 1u64 as *const String;
+    let _ = 1u64 as i8;
+    let _ = 1u64 as i16;
+    let _ = 1u64 as i32;
+    let _ = 1u64 as i64;
+    let _ = 1u64 as u8;
+    let _ = 1u64 as u16;
+    let _ = 1u64 as u32;
+    let _ = 1u64 as u64;
+    let _ = 1u64 as f32;
+    let _ = 1u64 as f64;
+
+    let _ = true as isize;
+    let _ = true as usize;
+    let _ = true as i8;
+    let _ = true as i16;
+    let _ = true as i32;
+    let _ = true as i64;
+    let _ = true as u8;
+    let _ = true as u16;
+    let _ = true as u32;
+    let _ = true as u64;
+
+    let _ = 1f32 as isize;
+    let _ = 1f32 as usize;
+    let _ = 1f32 as i8;
+    let _ = 1f32 as i16;
+    let _ = 1f32 as i32;
+    let _ = 1f32 as i64;
+    let _ = 1f32 as u8;
+    let _ = 1f32 as u16;
+    let _ = 1f32 as u32;
+    let _ = 1f32 as u64;
+    let _ = 1f32 as f32;
+    let _ = 1f32 as f64;
+
+    let _ = 1f64 as isize;
+    let _ = 1f64 as usize;
+    let _ = 1f64 as i8;
+    let _ = 1f64 as i16;
+    let _ = 1f64 as i32;
+    let _ = 1f64 as i64;
+    let _ = 1f64 as u8;
+    let _ = 1f64 as u16;
+    let _ = 1f64 as u32;
+    let _ = 1f64 as u64;
+    let _ = 1f64 as f32;
+    let _ = 1f64 as f64;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unsized_struct_cast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+pub struct Data([u8]);
+
+fn foo(x: &[u8]) {
+    let _: *const Data = x as *const Data;
+                       //^^^^^^^^^^^^^^^^ error: casting `&[u8]` as `*const Data` is invalid
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn unsupported_cast() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+struct A;
+
+fn main() {
+    let _ = 1.0 as *const A;
+          //^^^^^^^^^^^^^^^ error: casting `f64` as `*const A` is invalid
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn issue_17897() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    _ = ((), ()) as ();
+      //^^^^^^^^^^^^^^ error: non-primitive cast: `(_, _)` as `()`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_10991() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let nil = ();
+    let _t = nil as usize;
+           //^^^^^^^^^^^^ error: non-primitive cast: `()` as `usize`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_17444() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+enum Test {
+    Foo = 0
+}
+
+fn main() {
+    let _x = Test::Foo as *const isize;
+           //^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `Test` as `*const isize` is invalid
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_43825() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let error = error;
+              //^^^^^ error: no such value in this scope
+
+    0 as f32;
+    0.0 as u32;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_84213() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+struct Something {
+    pub field: u32,
+}
+
+fn main() {
+    let mut something = Something { field: 1337 };
+    let _ = something.field;
+
+    let _pointer_to_something = something as *const Something;
+                              //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*const Something`
+
+    let _mut_pointer_to_something = something as *mut Something;
+                                  //^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*mut Something`
+}
+"#,
+        );
+
+        // Fixed
+        check_diagnostics(
+            r#"
+//- minicore: sized
+struct Something {
+    pub field: u32,
+}
+
+fn main() {
+    let mut something = Something { field: 1337 };
+    let _ = something.field;
+
+    let _pointer_to_something = &something as *const Something;
+
+    let _mut_pointer_to_something = &mut something as *mut Something;
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_88621() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+#[repr(u8)]
+enum Kind2 {
+    Foo() = 1,
+    Bar{} = 2,
+    Baz = 3,
+}
+
+fn main() {
+    let _ = Kind2::Foo() as u8;
+          //^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Kind2` as `u8`
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_89497() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let pointer: usize = &1_i32 as *const i32 as usize;
+    let _reference: &'static i32 = unsafe { pointer as *const i32 as &'static i32 };
+                                          //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&i32`
+}
+"#,
+        );
+
+        // Fixed
+        check_diagnostics(
+            r#"
+//- minicore: sized
+fn main() {
+    let pointer: usize = &1_i32 as *const i32 as usize;
+    let _reference: &'static i32 = unsafe { &*(pointer as *const i32) };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn rustc_issue_106883() {
+        check_diagnostics_with_disabled(
+            r#"
+//- minicore: sized, deref
+use core::ops::Deref;
+
+struct Foo;
+
+impl Deref for Foo {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        &[]
+    }
+}
+
+fn main() {
+    let _ = "foo" as bool;
+          //^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first
+
+    let _ = Foo as bool;
+          //^^^^^^^^^^^ error: non-primitive cast: `Foo` as `bool`
+}
+
+fn _slice(bar: &[i32]) -> bool {
+    bar as bool
+  //^^^^^^^^^^^ error: casting `&[i32]` as `bool` is invalid: needs casting through a raw pointer first
+}
+"#,
+            &["E0308"],
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 7e70a27f789..9a9bab091d2 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -99,8 +99,9 @@ mod tests {
     fn missing_unsafe_diagnostic_with_raw_ptr() {
         check_diagnostics(
             r#"
+//- minicore: sized
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     unsafe { let _y = *x; }
     let _z = *x;
 }          //^^💡 error: this operation is unsafe and requires an unsafe function or block
@@ -112,17 +113,18 @@ fn main() {
     fn missing_unsafe_diagnostic_with_unsafe_call() {
         check_diagnostics(
             r#"
+//- minicore: sized
 struct HasUnsafe;
 
 impl HasUnsafe {
     unsafe fn unsafe_fn(&self) {
-        let x = &5 as *const usize;
+        let x = &5_usize as *const usize;
         let _y = *x;
     }
 }
 
 unsafe fn unsafe_fn() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     let _y = *x;
 }
 
@@ -250,14 +252,15 @@ fn main() {
     fn add_unsafe_block_when_dereferencing_a_raw_pointer() {
         check_fix(
             r#"
+//- minicore: sized
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     let _z = *x$0;
 }
 "#,
             r#"
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     let _z = unsafe { *x };
 }
 "#,
@@ -268,8 +271,9 @@ fn main() {
     fn add_unsafe_block_when_calling_unsafe_function() {
         check_fix(
             r#"
+//- minicore: sized
 unsafe fn func() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     let z = *x;
 }
 fn main() {
@@ -278,7 +282,7 @@ fn main() {
 "#,
             r#"
 unsafe fn func() {
-    let x = &5 as *const usize;
+    let x = &5_usize as *const usize;
     let z = *x;
 }
 fn main() {
@@ -292,6 +296,7 @@ fn main() {
     fn add_unsafe_block_when_calling_unsafe_method() {
         check_fix(
             r#"
+//- minicore: sized
 struct S(usize);
 impl S {
     unsafe fn func(&self) {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 06c6b0f3e4c..a9ff06fb0ab 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -190,4 +190,16 @@ fn foo(mut slice: &[u32]) -> usize {
 "#,
         );
     }
+
+    #[test]
+    fn regression_16564() {
+        check_diagnostics(
+            r#"
+//- minicore: copy
+fn test() {
+    let _x = (&(&mut (),)).0 as *const ();
+}
+            "#,
+        )
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
index 9b50a435e4c..4749718f1b1 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs
@@ -30,6 +30,7 @@ mod handlers {
     pub(crate) mod inactive_code;
     pub(crate) mod incoherent_impl;
     pub(crate) mod incorrect_case;
+    pub(crate) mod invalid_cast;
     pub(crate) mod invalid_derive_target;
     pub(crate) mod macro_error;
     pub(crate) mod malformed_derive;
@@ -390,6 +391,7 @@ pub fn semantic_diagnostics(
     for diag in diags {
         let d = match diag {
             AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d),
+            AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d),
             AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d),
             AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) {
                 Some(it) => it,
@@ -397,6 +399,7 @@ pub fn semantic_diagnostics(
             }
             AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d),
             AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d),
+            AnyDiagnostic::InvalidCast(d) => handlers::invalid_cast::invalid_cast(&ctx, &d),
             AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d),
             AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d),
             AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d),
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index dc390f8f67a..c37c469dff4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -313,6 +313,7 @@ fn main() {
                        //^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
                        //^^^^^^^^^^^^(
                        //^^^^^^^^^^^^)
+                       //^^^^<fn-item-to-fn-pointer>
     let _: fn()        = || {};
                        //^^^^^<closure-to-fn-pointer>
     let _: unsafe fn() = || {};
@@ -321,6 +322,8 @@ fn main() {
                        //^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
                        //^^^^^^^^^^^^^^^^^^^^^(
                        //^^^^^^^^^^^^^^^^^^^^^)
+                       //^^^^^^^^^&raw mut $
+                       //^^^^^^^^^*
     let _: &mut [_]    = &mut [0; 0];
                        //^^^^^^^^^^^<unsize>
                        //^^^^^^^^^^^&mut $
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
index 2070022d418..ad7fbb00e5c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs
@@ -554,6 +554,7 @@ fn main() {
 fn test_unsafe_highlighting() {
     check_highlighting(
         r#"
+//- minicore: sized
 macro_rules! id {
     ($($tt:tt)*) => {
         $($tt)*