From 9cfea3ec291365743f50802bf28bc6106b4a255d Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Mon, 24 Mar 2025 02:34:24 +0900 Subject: [PATCH] fix: Yet another false positive invalid cast diagnostic --- crates/hir-ty/src/infer/cast.rs | 19 ++-- crates/hir-ty/src/infer/unify.rs | 95 ++++++++++++++++--- .../src/handlers/invalid_cast.rs | 46 ++++++++- 3 files changed, 134 insertions(+), 26 deletions(-) diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index d3de86f03872..0670a4ec3df1 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -50,7 +50,7 @@ impl CastTy { None } } - TyKind::Raw(m, ty) => Some(Self::Ptr(table.resolve_ty_shallow(ty), *m)), + TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)), TyKind::Function(_) => Some(Self::FnPtr), _ => None, } @@ -105,9 +105,8 @@ impl CastCheck { F: FnMut(ExprId, Vec), 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); + self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); + self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { return Ok(()); @@ -153,7 +152,7 @@ impl CastCheck { (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 sig = table.eagerly_normalize_and_resolve_shallow_in(sig); let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes) { @@ -165,7 +164,6 @@ impl CastCheck { (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( @@ -180,13 +178,13 @@ impl CastCheck { }, // array-ptr-cast CastTy::Ptr(t, m) => { - let t = table.resolve_ty_shallow(&t); + let t = table.eagerly_normalize_and_resolve_shallow_in(t); if !table.is_sized(&t) { return Err(CastError::IllegalCast); } self.check_ref_cast( table, - &inner_ty, + inner_ty, *mutbl, &t, m, @@ -359,7 +357,7 @@ impl CastCheck { } } -#[derive(PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq)] enum PointerKind { // thin pointer Thin, @@ -373,8 +371,7 @@ enum PointerKind { } fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { - let ty = table.resolve_ty_shallow(ty); - let ty = table.normalize_associated_types_in(ty); + let ty = table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); if table.is_sized(&ty) { return Ok(Some(PointerKind::Thin)); diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 903097ee2f8f..67796b96962a 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -364,6 +364,64 @@ impl<'a> InferenceTable<'a> { ) } + /// Works almost same as [`Self::normalize_associated_types_in`], but this also resolves shallow + /// the inference variables + pub(crate) fn eagerly_normalize_and_resolve_shallow_in(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { + fn eagerly_resolve_ty( + table: &mut InferenceTable<'_>, + ty: Ty, + mut tys: SmallVec<[Ty; N]>, + ) -> Ty { + if tys.contains(&ty) { + return ty; + } + tys.push(ty.clone()); + + match ty.kind(Interner) { + TyKind::Alias(AliasTy::Projection(proj_ty)) => { + let ty = table.normalize_projection_ty(proj_ty.clone()); + eagerly_resolve_ty(table, ty, tys) + } + TyKind::InferenceVar(..) => { + let ty = table.resolve_ty_shallow(&ty); + eagerly_resolve_ty(table, ty, tys) + } + _ => ty, + } + } + + fold_tys_and_consts( + ty, + |e, _| match e { + Either::Left(ty) => { + Either::Left(eagerly_resolve_ty::<8>(self, ty, SmallVec::new())) + } + Either::Right(c) => Either::Right(match &c.data(Interner).value { + chalk_ir::ConstValue::Concrete(cc) => match &cc.interned { + crate::ConstScalar::UnevaluatedConst(c_id, subst) => { + // FIXME: same as `normalize_associated_types_in` + if subst.len(Interner) == 0 { + if let Ok(eval) = self.db.const_eval(*c_id, subst.clone(), None) { + eval + } else { + unknown_const(c.data(Interner).ty.clone()) + } + } else { + unknown_const(c.data(Interner).ty.clone()) + } + } + _ => c, + }, + _ => c, + }), + }, + DebruijnIndex::INNERMOST, + ) + } + pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { let var = self.new_type_var(); let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; @@ -918,7 +976,26 @@ impl<'a> InferenceTable<'a> { /// Check if given type is `Sized` or not pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + fn short_circuit_trivial_tys(ty: &Ty) -> Option { + match ty.kind(Interner) { + TyKind::Scalar(..) + | TyKind::Ref(..) + | TyKind::Raw(..) + | TyKind::Never + | TyKind::FnDef(..) + | TyKind::Array(..) + | TyKind::Function(..) => Some(true), + TyKind::Slice(..) | TyKind::Str | TyKind::Dyn(..) => Some(false), + _ => None, + } + } + let mut ty = ty.clone(); + ty = self.eagerly_normalize_and_resolve_shallow_in(ty); + if let Some(sized) = short_circuit_trivial_tys(&ty) { + return sized; + } + { let mut structs = SmallVec::<[_; 8]>::new(); // Must use a loop here and not recursion because otherwise users will conduct completely @@ -937,26 +1014,16 @@ impl<'a> InferenceTable<'a> { // Structs can have DST as its last field and such cases are not handled // as unsized by the chalk, so we do this manually. ty = last_field_ty; + ty = self.eagerly_normalize_and_resolve_shallow_in(ty); + if let Some(sized) = short_circuit_trivial_tys(&ty) { + return sized; + } } else { break; }; } } - // Early return for some obvious types - if matches!( - ty.kind(Interner), - TyKind::Scalar(..) - | TyKind::Ref(..) - | TyKind::Raw(..) - | TyKind::Never - | TyKind::FnDef(..) - | TyKind::Array(..) - | TyKind::Function(_) - ) { - return true; - } - let Some(sized) = self .db .lang_item(self.trait_env.krate, LangItem::Sized) diff --git a/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 82cd1f2fde6d..b56255b1fde4 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -440,8 +440,9 @@ fn main() { q as *const [i32]; //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` + // FIXME: This should emit diagnostics but disabled to prevent many false positives let t: *mut (dyn Trait + 'static) = 0 as *mut _; - //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut _` + let mut fail: *const str = 0 as *const str; //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str` let mut fail2: *const str = 0isize as *const str; @@ -1161,6 +1162,49 @@ struct ZerocopyKnownLayoutMaybeUninit(<::Type as KnownLayout> fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit { ptr as *mut _ } +"#, + ); + } + + #[test] + fn regression_19431() { + check_diagnostics( + r#" +//- minicore: coerce_unsized +struct Dst([u8]); + +struct Struct { + body: Dst, +} + +trait Field { + type Type: ?Sized; +} + +impl Field for Struct { + type Type = Dst; +} + +trait KnownLayout { + type MaybeUninit: ?Sized; + type PointerMetadata; +} + +impl KnownLayout for [T] { + type MaybeUninit = [T]; + type PointerMetadata = usize; +} + +impl KnownLayout for Dst { + type MaybeUninit = Dst; + type PointerMetadata = <[u8] as KnownLayout>::PointerMetadata; +} + +struct ZerocopyKnownLayoutMaybeUninit(<::Type as KnownLayout>::MaybeUninit); + +fn test(ptr: *mut ZerocopyKnownLayoutMaybeUninit) -> *mut <::Type as KnownLayout>::MaybeUninit { + ptr as *mut _ +} "#, ); }