fix: Yet another false positive invalid cast diagnostic
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs
index d3de86f..0670a4e 100644
--- a/crates/hir-ty/src/infer/cast.rs
+++ b/crates/hir-ty/src/infer/cast.rs
@@ -50,7 +50,7 @@
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 @@
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);
+ 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 @@
(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 @@
(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 @@
},
// 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 @@
}
}
-#[derive(PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq)]
enum PointerKind {
// thin pointer
Thin,
@@ -373,8 +371,7 @@
}
fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
- 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 903097e..67796b9 100644
--- a/crates/hir-ty/src/infer/unify.rs
+++ b/crates/hir-ty/src/infer/unify.rs
@@ -364,6 +364,64 @@
)
}
+ /// 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<T>(&mut self, ty: T) -> T
+ where
+ T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
+ {
+ fn eagerly_resolve_ty<const N: usize>(
+ 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 @@
/// 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<bool> {
+ 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 @@
// 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 82cd1f2..b56255b 100644
--- a/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -440,8 +440,9 @@
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;
@@ -1164,4 +1165,47 @@
"#,
);
}
+
+ #[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<T> KnownLayout for [T] {
+ type MaybeUninit = [T];
+ type PointerMetadata = usize;
+}
+
+impl KnownLayout for Dst {
+ type MaybeUninit = Dst;
+ type PointerMetadata = <[u8] as KnownLayout>::PointerMetadata;
+}
+
+struct ZerocopyKnownLayoutMaybeUninit(<<Struct as Field>::Type as KnownLayout>::MaybeUninit);
+
+fn test(ptr: *mut ZerocopyKnownLayoutMaybeUninit) -> *mut <<Struct as Field>::Type as KnownLayout>::MaybeUninit {
+ ptr as *mut _
+}
+"#,
+ );
+ }
}