mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-31 20:09:01 +00:00
Merge pull request #19432 from ShoyuVanilla/issue-19431
fix: Yet another false positive invalid cast diagnostic
This commit is contained in:
commit
a2783704bf
3 changed files with 134 additions and 26 deletions
|
|
@ -50,7 +50,7 @@ impl CastTy {
|
||||||
None
|
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),
|
TyKind::Function(_) => Some(Self::FnPtr),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
|
|
@ -105,9 +105,8 @@ impl CastCheck {
|
||||||
F: FnMut(ExprId, Vec<Adjustment>),
|
F: FnMut(ExprId, Vec<Adjustment>),
|
||||||
G: FnMut(ExprId),
|
G: FnMut(ExprId),
|
||||||
{
|
{
|
||||||
table.resolve_obligations_as_possible();
|
self.expr_ty = table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone());
|
||||||
self.expr_ty = table.resolve_ty_shallow(&self.expr_ty);
|
self.cast_ty = table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone());
|
||||||
self.cast_ty = table.resolve_ty_shallow(&self.cast_ty);
|
|
||||||
|
|
||||||
if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() {
|
if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
|
@ -153,7 +152,7 @@ impl CastCheck {
|
||||||
(None, Some(t_cast)) => match self.expr_ty.kind(Interner) {
|
(None, Some(t_cast)) => match self.expr_ty.kind(Interner) {
|
||||||
TyKind::FnDef(..) => {
|
TyKind::FnDef(..) => {
|
||||||
let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig");
|
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);
|
let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner);
|
||||||
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes)
|
if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr, CoerceNever::Yes)
|
||||||
{
|
{
|
||||||
|
|
@ -165,7 +164,6 @@ impl CastCheck {
|
||||||
(CastTy::FnPtr, t_cast)
|
(CastTy::FnPtr, t_cast)
|
||||||
}
|
}
|
||||||
TyKind::Ref(mutbl, _, inner_ty) => {
|
TyKind::Ref(mutbl, _, inner_ty) => {
|
||||||
let inner_ty = table.resolve_ty_shallow(inner_ty);
|
|
||||||
return match t_cast {
|
return match t_cast {
|
||||||
CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) {
|
CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) {
|
||||||
TyKind::Scalar(
|
TyKind::Scalar(
|
||||||
|
|
@ -180,13 +178,13 @@ impl CastCheck {
|
||||||
},
|
},
|
||||||
// array-ptr-cast
|
// array-ptr-cast
|
||||||
CastTy::Ptr(t, m) => {
|
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) {
|
if !table.is_sized(&t) {
|
||||||
return Err(CastError::IllegalCast);
|
return Err(CastError::IllegalCast);
|
||||||
}
|
}
|
||||||
self.check_ref_cast(
|
self.check_ref_cast(
|
||||||
table,
|
table,
|
||||||
&inner_ty,
|
inner_ty,
|
||||||
*mutbl,
|
*mutbl,
|
||||||
&t,
|
&t,
|
||||||
m,
|
m,
|
||||||
|
|
@ -359,7 +357,7 @@ impl CastCheck {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
enum PointerKind {
|
enum PointerKind {
|
||||||
// thin pointer
|
// thin pointer
|
||||||
Thin,
|
Thin,
|
||||||
|
|
@ -373,8 +371,7 @@ enum PointerKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
|
fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> {
|
||||||
let ty = table.resolve_ty_shallow(ty);
|
let ty = table.eagerly_normalize_and_resolve_shallow_in(ty.clone());
|
||||||
let ty = table.normalize_associated_types_in(ty);
|
|
||||||
|
|
||||||
if table.is_sized(&ty) {
|
if table.is_sized(&ty) {
|
||||||
return Ok(Some(PointerKind::Thin));
|
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 {
|
pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty {
|
||||||
let var = self.new_type_var();
|
let var = self.new_type_var();
|
||||||
let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() };
|
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
|
/// Check if given type is `Sized` or not
|
||||||
pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
|
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();
|
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();
|
let mut structs = SmallVec::<[_; 8]>::new();
|
||||||
// Must use a loop here and not recursion because otherwise users will conduct completely
|
// 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
|
// Structs can have DST as its last field and such cases are not handled
|
||||||
// as unsized by the chalk, so we do this manually.
|
// as unsized by the chalk, so we do this manually.
|
||||||
ty = last_field_ty;
|
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 {
|
} else {
|
||||||
break;
|
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
|
let Some(sized) = self
|
||||||
.db
|
.db
|
||||||
.lang_item(self.trait_env.krate, LangItem::Sized)
|
.lang_item(self.trait_env.krate, LangItem::Sized)
|
||||||
|
|
|
||||||
|
|
@ -440,8 +440,9 @@ fn main() {
|
||||||
q as *const [i32];
|
q as *const [i32];
|
||||||
//^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*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 _;
|
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;
|
let mut fail: *const str = 0 as *const str;
|
||||||
//^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str`
|
//^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str`
|
||||||
let mut fail2: *const str = 0isize as *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 {
|
fn test(ptr: *mut [u8]) -> *mut ZerocopyKnownLayoutMaybeUninit {
|
||||||
ptr as *mut _
|
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