mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-11-04 05:35:55 +00:00 
			
		
		
		
	fix: Yet another false positive invalid cast diagnostic
This commit is contained in:
		
							parent
							
								
									37acea8052
								
							
						
					
					
						commit
						9cfea3ec29
					
				
					 3 changed files with 134 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -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<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 @@ 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<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));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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<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 @@ 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<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 @@ 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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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(<<Flexible as Field>::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<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 _
 | 
			
		||||
}
 | 
			
		||||
"#,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue