Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/cast.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/cast.rs | 347 |
1 files changed, 169 insertions, 178 deletions
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 4cd6144a14..990281a7c8 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -1,17 +1,18 @@ //! Type cast logic. Basically coercion + additional casts. -use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags}; +use rustc_ast_ir::Mutability; +use rustc_type_ir::{ + Flags, InferTy, TypeFlags, UintTy, + inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, SliceLike, Ty as _}, +}; use stdx::never; -use crate::infer::coerce::CoerceNever; use crate::{ - Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, QuantifiedWhereClauses, Ty, - TyExt, TyKind, TypeFlags, WhereClause, + InferenceDiagnostic, db::HirDatabase, - from_chalk_trait_id, - infer::{AllowTwoPhase, InferenceContext}, - next_solver::mapping::ChalkToNextSolver, + infer::{AllowTwoPhase, InferenceContext, coerce::CoerceNever}, + next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind}, }; #[derive(Debug)] @@ -25,24 +26,24 @@ pub(crate) enum Int { } #[derive(Debug)] -pub(crate) enum CastTy { +pub(crate) enum CastTy<'db> { Int(Int), Float, FnPtr, - Ptr(Ty, Mutability), + Ptr(Ty<'db>, Mutability), // `DynStar` is Not supported yet in r-a } -impl CastTy { - pub(crate) fn from_ty(db: &dyn HirDatabase, 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), +impl<'db> CastTy<'db> { + pub(crate) fn from_ty(db: &dyn HirDatabase, t: Ty<'db>) -> Option<Self> { + match t.kind() { + TyKind::Bool => Some(Self::Int(Int::Bool)), + TyKind::Char => Some(Self::Int(Int::Char)), + TyKind::Int(_) => Some(Self::Int(Int::I)), + TyKind::Uint(it) => Some(Self::Int(Int::U(it))), + TyKind::Infer(InferTy::IntVar(_)) => Some(Self::Int(Int::InferenceVar)), + TyKind::Infer(InferTy::FloatVar(_)) => Some(Self::Float), + TyKind::Float(_) => Some(Self::Float), TyKind::Adt(..) => { let (AdtId::EnumId(id), _) = t.as_adt()? else { return None; @@ -50,8 +51,8 @@ impl CastTy { let enum_data = id.enum_variants(db); if enum_data.is_payload_free(db) { Some(Self::Int(Int::CEnum)) } else { None } } - TyKind::Raw(m, ty) => Some(Self::Ptr(ty.clone(), *m)), - TyKind::Function(_) => Some(Self::FnPtr), + TyKind::RawPtr(ty, m) => Some(Self::Ptr(ty, m)), + TyKind::FnPtr(..) => Some(Self::FnPtr), _ => None, } } @@ -77,37 +78,47 @@ pub enum CastError { } impl CastError { - fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic { + fn into_diagnostic<'db>( + self, + expr: ExprId, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, + ) -> InferenceDiagnostic<'db> { InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty } } } #[derive(Clone, Debug)] -pub(super) struct CastCheck { +pub(super) struct CastCheck<'db> { expr: ExprId, source_expr: ExprId, - expr_ty: Ty, - cast_ty: Ty, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, } -impl CastCheck { - pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self { +impl<'db> CastCheck<'db> { + pub(super) fn new( + expr: ExprId, + source_expr: ExprId, + expr_ty: Ty<'db>, + cast_ty: Ty<'db>, + ) -> Self { Self { expr, source_expr, expr_ty, cast_ty } } pub(super) fn check( &mut self, - ctx: &mut InferenceContext<'_>, - ) -> Result<(), InferenceDiagnostic> { - self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty.clone()); - self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty.clone()); + ctx: &mut InferenceContext<'_, 'db>, + ) -> Result<(), InferenceDiagnostic<'db>> { + self.expr_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.expr_ty); + self.cast_ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(self.cast_ty); // This should always come first so that we apply the coercion, which impacts infer vars. if ctx .coerce( self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - self.cast_ty.to_nextsolver(ctx.table.interner), + self.expr_ty, + self.cast_ty, AllowTwoPhase::No, CoerceNever::Yes, ) @@ -117,83 +128,82 @@ impl CastCheck { return Ok(()); } - if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + if self.expr_ty.references_non_lt_error() || self.cast_ty.references_non_lt_error() { return Ok(()); } - if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) - && !ctx.table.is_sized(&self.cast_ty) + if !self.cast_ty.flags().contains(TypeFlags::HAS_TY_INFER) + && !ctx.table.is_sized(self.cast_ty) { return Err(InferenceDiagnostic::CastToUnsized { expr: self.expr, - cast_ty: self.cast_ty.clone(), + cast_ty: self.cast_ty, }); } // Chalk doesn't support trait upcasting and fails to solve some obvious goals // when the trait environment contains some recursive traits (See issue #18047) // We skip cast checks for such cases for now, until the next-gen solver. - if contains_dyn_trait(&self.cast_ty) { + if contains_dyn_trait(self.cast_ty) { return Ok(()); } - self.do_check(ctx) - .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) + self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty)) } - fn do_check(&self, ctx: &mut InferenceContext<'_>) -> Result<(), CastError> { - let (t_from, t_cast) = match ( - CastTy::from_ty(ctx.db, &self.expr_ty), - CastTy::from_ty(ctx.db, &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(ctx.db).expect("FnDef had no sig"); - let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); - let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); - if ctx - .coerce( - self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - fn_ptr.to_nextsolver(ctx.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) - .is_ok() - { - } else { - return Err(CastError::IllegalCast); - } + fn do_check(&self, ctx: &mut InferenceContext<'_, 'db>) -> Result<(), CastError> { + let (t_from, t_cast) = + match (CastTy::from_ty(ctx.db, self.expr_ty), CastTy::from_ty(ctx.db, self.cast_ty)) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind() { + TyKind::FnDef(..) => { + let sig = + self.expr_ty.callable_sig(ctx.interner()).expect("FnDef had no sig"); + let sig = ctx.table.eagerly_normalize_and_resolve_shallow_in(sig); + let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty, + fn_ptr, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + } else { + return Err(CastError::IllegalCast); + } - (CastTy::FnPtr, t_cast) - } - TyKind::Ref(mutbl, _, 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 = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); - if !ctx.table.is_sized(&t) { - return Err(CastError::IllegalCast); + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(_, inner_ty, mutbl) => { + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind() { + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Infer(InferTy::IntVar(_) | InferTy::FloatVar(_)) => { + Err(CastError::NeedDeref) + } + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = ctx.table.eagerly_normalize_and_resolve_shallow_in(t); + if !ctx.table.is_sized(t) { + return Err(CastError::IllegalCast); + } + self.check_ref_cast(ctx, inner_ty, mutbl, t, m) } - self.check_ref_cast(ctx, inner_ty, *mutbl, &t, m) - } - _ => Err(CastError::NonScalar), - }; - } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + }, _ => return Err(CastError::NonScalar), - }, - _ => return Err(CastError::NonScalar), - }; + }; // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym @@ -207,10 +217,10 @@ impl CastCheck { } (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(ctx, &src, &dst), - (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, &src), - (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, &dst), - (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, &dst), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, 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(()), @@ -220,23 +230,23 @@ impl CastCheck { fn check_ref_cast( &self, - ctx: &mut InferenceContext<'_>, - t_expr: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + t_expr: Ty<'db>, m_expr: Mutability, - t_cast: &Ty, + t_cast: Ty<'db>, m_cast: Mutability, ) -> Result<(), CastError> { // Mutability order is opposite to rustc. `Mut < Not` if m_expr <= m_cast - && let TyKind::Array(ety, _) = t_expr.kind(Interner) + && let TyKind::Array(ety, _) = t_expr.kind() { // 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); + let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); if ctx .coerce( self.source_expr.into(), - self.expr_ty.to_nextsolver(ctx.table.interner), - array_ptr_type.to_nextsolver(ctx.table.interner), + self.expr_ty, + array_ptr_type, AllowTwoPhase::No, CoerceNever::Yes, ) @@ -253,13 +263,7 @@ impl CastCheck { // This is a less strict condition than rustc's `demand_eqtype`, // but false negative is better than false positive if ctx - .coerce( - self.source_expr.into(), - ety.to_nextsolver(ctx.table.interner), - t_cast.to_nextsolver(ctx.table.interner), - AllowTwoPhase::No, - CoerceNever::Yes, - ) + .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, CoerceNever::Yes) .is_ok() { return Ok(()); @@ -271,9 +275,9 @@ impl CastCheck { fn check_ptr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - src: &Ty, - dst: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + src: Ty<'db>, + dst: Ty<'db>, ) -> Result<(), CastError> { let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; @@ -286,24 +290,13 @@ impl CastCheck { (_, Some(PointerKind::Thin)) => Ok(()), (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)) { + match (src_tty.principal_def_id(), dst_tty.principal_def_id()) { (Some(src_principal), Some(dst_principal)) => { if src_principal == dst_principal { return Ok(()); } - let src_principal = - ctx.db.trait_signature(from_chalk_trait_id(src_principal)); - let dst_principal = - ctx.db.trait_signature(from_chalk_trait_id(dst_principal)); + let src_principal = ctx.db.trait_signature(src_principal.0); + let dst_principal = ctx.db.trait_signature(dst_principal.0); if src_principal.flags.contains(TraitFlags::AUTO) && dst_principal.flags.contains(TraitFlags::AUTO) { @@ -322,8 +315,8 @@ impl CastCheck { fn check_ptr_addr_cast( &self, - ctx: &mut InferenceContext<'_>, - expr_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + expr_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), @@ -336,8 +329,8 @@ impl CastCheck { fn check_addr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), @@ -352,8 +345,8 @@ impl CastCheck { fn check_fptr_ptr_cast( &self, - ctx: &mut InferenceContext<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), @@ -366,30 +359,34 @@ impl CastCheck { } #[derive(Debug, PartialEq, Eq)] -enum PointerKind { +enum PointerKind<'db> { // thin pointer Thin, // trait object - VTable(Binders<QuantifiedWhereClauses>), + VTable(BoundExistentialPredicates<'db>), // slice Length, OfAlias, - OfParam(PlaceholderIndex), + OfParam(ParamTy), Error, } -fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result<Option<PointerKind>, ()> { - let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty.clone()); +fn pointer_kind<'db>( + ty: Ty<'db>, + ctx: &mut InferenceContext<'_, 'db>, +) -> Result<Option<PointerKind<'db>>, ()> { + let ty = ctx.table.eagerly_normalize_and_resolve_shallow_in(ty); - if ctx.table.is_sized(&ty) { + if ctx.table.is_sized(ty) { return Ok(Some(PointerKind::Thin)); } - match ty.kind(Interner) { + match ty.kind() { 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 { + TyKind::Dynamic(bounds, _) => Ok(Some(PointerKind::VTable(bounds))), + TyKind::Adt(adt_def, subst) => { + let id = adt_def.def_id().0; + let AdtId::StructId(id) = id else { never!("`{:?}` should be sized but is not?", ty); return Err(()); }; @@ -397,69 +394,63 @@ fn pointer_kind(ty: &Ty, ctx: &mut InferenceContext<'_>) -> Result<Option<Pointe let struct_data = id.fields(ctx.db); if let Some((last_field, _)) = struct_data.fields().iter().last() { let last_field_ty = - ctx.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); - pointer_kind(&last_field_ty, ctx) + ctx.db.field_types_ns(id.into())[last_field].instantiate(ctx.interner(), subst); + pointer_kind(last_field_ty, ctx) } 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, ctx), - } - } + TyKind::Tuple(subst) => match subst.iter().next_back() { + None => Ok(Some(PointerKind::Thin)), + Some(ty) => pointer_kind(ty, ctx), + }, 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::Alias(..) => Ok(Some(PointerKind::OfAlias)), + TyKind::Error(_) => Ok(Some(PointerKind::Error)), + TyKind::Param(idx) => Ok(Some(PointerKind::OfParam(idx))), + TyKind::Bound(..) | TyKind::Placeholder(..) | TyKind::Infer(..) => Ok(None), + TyKind::Int(_) + | TyKind::Uint(_) + | TyKind::Float(_) + | TyKind::Bool + | TyKind::Char | TyKind::Array(..) | TyKind::CoroutineWitness(..) - | TyKind::Raw(..) + | TyKind::RawPtr(..) | TyKind::Ref(..) | TyKind::FnDef(..) - | TyKind::Function(_) + | TyKind::FnPtr(..) | TyKind::Closure(..) | TyKind::Coroutine(..) + | TyKind::CoroutineClosure(..) | TyKind::Never => { never!("`{:?}` should be sized but is not?", ty); Err(()) } + TyKind::UnsafeBinder(..) | TyKind::Pat(..) => { + never!("we don't produce these types: {ty:?}"); + Err(()) + } } } -fn contains_dyn_trait(ty: &Ty) -> bool { +fn contains_dyn_trait<'db>(ty: Ty<'db>) -> bool { use std::ops::ControlFlow; - use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, - }; + use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; struct DynTraitVisitor; - impl TypeVisitor<Interner> for DynTraitVisitor { - type BreakTy = (); - - fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> { - self - } - - fn interner(&self) -> Interner { - Interner - } + impl<'db> TypeVisitor<DbInterner<'db>> for DynTraitVisitor { + type Result = ControlFlow<()>; - fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow<Self::BreakTy> { - match ty.kind(Interner) { - TyKind::Dyn(_) => ControlFlow::Break(()), - _ => ty.super_visit_with(self.as_dyn(), outer_binder), + fn visit_ty(&mut self, ty: Ty<'db>) -> ControlFlow<()> { + match ty.kind() { + TyKind::Dynamic(..) => ControlFlow::Break(()), + _ => ty.super_visit_with(self), } } } - ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break() + ty.visit_with(&mut DynTraitVisitor).is_break() } |