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 | 397 |
1 files changed, 200 insertions, 197 deletions
diff --git a/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 4e95eca3f9..c128977d7b 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/crates/hir-ty/src/infer/cast.rs @@ -1,13 +1,18 @@ //! Type cast logic. Basically coercion + additional casts. -use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; -use hir_def::{AdtId, hir::ExprId}; +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::{ - Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, PlaceholderIndex, - QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, - infer::{coerce::CoerceNever, unify::InferenceTable}, + InferenceDiagnostic, + db::HirDatabase, + infer::{AllowTwoPhase, InferenceContext, coerce::CoerceNever}, + next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind}, }; #[derive(Debug)] @@ -21,33 +26,33 @@ 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(table: &mut InferenceTable<'_>, 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; }; - let enum_data = id.enum_variants(table.db); - if enum_data.is_payload_free(table.db) { Some(Self::Int(Int::CEnum)) } else { None } + 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, } } @@ -73,119 +78,124 @@ 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<F, G>( + pub(super) fn check( &mut self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - set_coercion_cast: &mut G, - ) -> Result<(), InferenceDiagnostic> - where - F: FnMut(ExprId, Vec<Adjustment>), - G: FnMut(ExprId), - { - 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() { + 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, + self.cast_ty, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + ctx.result.coercion_casts.insert(self.source_expr); + return Ok(()); + } + + 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) - && !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) { - return Ok(()); - } - - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty, CoerceNever::Yes) { - apply_adjustments(self.source_expr, adj); - set_coercion_cast(self.source_expr); + if contains_dyn_trait(self.cast_ty) { return Ok(()); } - self.do_check(table, apply_adjustments) - .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<F>( - &self, - table: &mut InferenceTable<'_>, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec<Adjustment>), - { + fn do_check(&self, ctx: &mut InferenceContext<'_, 'db>) -> Result<(), CastError> { let (t_from, t_cast) = - match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { + 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) { + (None, Some(t_cast)) => match self.expr_ty.kind() { TyKind::FnDef(..) => { - let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no 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) + 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() { - apply_adjustments(self.source_expr, adj); } else { return Err(CastError::IllegalCast); } (CastTy::FnPtr, t_cast) } - TyKind::Ref(mutbl, _, inner_ty) => { + TyKind::Ref(_, inner_ty, mutbl) => { 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), + 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 = table.eagerly_normalize_and_resolve_shallow_in(t); - if !table.is_sized(&t) { + 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( - table, - inner_ty, - *mutbl, - &t, - m, - apply_adjustments, - ) + self.check_ref_cast(ctx, inner_ty, mutbl, t, m) } _ => Err(CastError::NonScalar), }; @@ -207,12 +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(table, &src, &dst) - } - (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), - (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), - (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &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,39 +228,45 @@ impl CastCheck { } } - fn check_ref_cast<F>( + fn check_ref_cast( &self, - table: &mut InferenceTable<'_>, - t_expr: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + t_expr: Ty<'db>, m_expr: Mutability, - t_cast: &Ty, + t_cast: Ty<'db>, m_cast: Mutability, - apply_adjustments: &mut F, - ) -> Result<(), CastError> - where - F: FnMut(ExprId, Vec<Adjustment>), - { + ) -> Result<(), CastError> { // Mutability order is opposite to rustc. `Mut < Not` - if m_expr <= m_cast { - if let TyKind::Array(ety, _) = t_expr.kind(Interner) { - // 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); - if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type, CoerceNever::Yes) - { - apply_adjustments(self.source_expr, adj); - } else { - never!( - "could not cast from reference to array to pointer to array ({:?} to {:?})", - self.expr_ty, - array_ptr_type - ); - } + if m_expr <= m_cast + && let TyKind::Array(ety, _) = t_expr.kind() + { + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr); + if ctx + .coerce( + self.source_expr.into(), + self.expr_ty, + array_ptr_type, + AllowTwoPhase::No, + CoerceNever::Yes, + ) + .is_ok() + { + } else { + never!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type + ); + } - // This is a less strict condition than rustc's `demand_eqtype`, - // but false negative is better than false positive - if table.coerce(ety, t_cast, CoerceNever::Yes).is_ok() { - return Ok(()); - } + // 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, t_cast, AllowTwoPhase::No, CoerceNever::Yes) + .is_ok() + { + return Ok(()); } } @@ -261,12 +275,12 @@ impl CastCheck { fn check_ptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, - src: &Ty, - dst: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + src: Ty<'db>, + dst: Ty<'db>, ) -> Result<(), CastError> { - let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; - let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + let src_kind = pointer_kind(src, ctx).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, ctx).map_err(|_| CastError::Unknown)?; match (src_kind, dst_kind) { (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), @@ -276,25 +290,16 @@ 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 = - table.db.trait_datum(table.trait_env.krate, src_principal); - let dst_principal = - table.db.trait_datum(table.trait_env.krate, dst_principal); - if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + 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) + { Ok(()) } else { Err(CastError::DifferingKinds) @@ -310,10 +315,10 @@ impl CastCheck { fn check_ptr_addr_cast( &self, - table: &mut InferenceTable<'_>, - expr_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + expr_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(expr_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownExprPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -324,10 +329,10 @@ impl CastCheck { fn check_addr_ptr_cast( &self, - table: &mut InferenceTable<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -340,10 +345,10 @@ impl CastCheck { fn check_fptr_ptr_cast( &self, - table: &mut InferenceTable<'_>, - cast_ty: &Ty, + ctx: &mut InferenceContext<'_, 'db>, + cast_ty: Ty<'db>, ) -> Result<(), CastError> { - match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + match pointer_kind(cast_ty, ctx).map_err(|_| CastError::Unknown)? { // None => Err(CastError::UnknownCastPtrKind), None => Ok(()), Some(PointerKind::Error) => Ok(()), @@ -354,100 +359,98 @@ 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, table: &mut InferenceTable<'_>) -> Result<Option<PointerKind>, ()> { - let ty = 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 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(()); }; - let struct_data = id.fields(table.db); + let struct_data = id.fields(ctx.db); if let Some((last_field, _)) = struct_data.fields().iter().last() { let last_field_ty = - table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); - pointer_kind(&last_field_ty, table) + ctx.db.field_types(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, table), - } - } + 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() } |