Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/coerce.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/coerce.rs | 623 |
1 files changed, 357 insertions, 266 deletions
diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index 7930d8b0ed..df24148920 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -35,48 +35,60 @@ //! // and are then unable to coerce `&7i32` to `&mut i32`. //! ``` -use chalk_ir::cast::Cast; use hir_def::{ CallableDefId, + attrs::AttrFlags, hir::{ExprId, ExprOrPatId}, - lang_item::LangItem, signatures::FunctionSignature, }; -use intern::sym; use rustc_ast_ir::Mutability; use rustc_type_ir::{ - TypeAndMut, + BoundVar, DebruijnIndex, TyVid, TypeAndMut, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, error::TypeError, - inherent::{IntoKind, Safety, Ty as _}, + inherent::{Const as _, GenericArg as _, IntoKind, Safety, SliceLike, Ty as _}, }; use smallvec::{SmallVec, smallvec}; use tracing::{debug, instrument}; use triomphe::Arc; use crate::{ - Adjust, Adjustment, AutoBorrow, Interner, PointerCast, TargetFeatures, TraitEnvironment, + Adjust, Adjustment, AutoBorrow, PointerCast, TargetFeatures, TraitEnvironment, autoderef::Autoderef, db::{HirDatabase, InternedClosureId}, - infer::{AllowTwoPhase, InferenceContext, TypeMismatch, unify::InferenceTable}, + infer::{ + AllowTwoPhase, AutoBorrowMutability, InferenceContext, TypeMismatch, expr::ExprIsRead, + }, next_solver::{ - Binder, CallableIdWrapper, ClauseKind, CoercePredicate, DbInterner, ErrorGuaranteed, - GenericArgs, PolyFnSig, PredicateKind, Region, SolverDefId, TraitRef, Ty, TyKind, + Binder, BoundConst, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, CallableIdWrapper, + Canonical, ClauseKind, CoercePredicate, Const, ConstKind, DbInterner, ErrorGuaranteed, + GenericArgs, PolyFnSig, PredicateKind, Region, RegionKind, TraitRef, Ty, TyKind, + TypingMode, infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, InferResult, + DbInternerInferExt, InferCtxt, InferOk, InferResult, relate::RelateResult, select::{ImplSource, SelectionError}, traits::{Obligation, ObligationCause, PredicateObligation, PredicateObligations}, }, - mapping::{ChalkToNextSolver, NextSolverToChalk}, obligation_ctxt::ObligationCtxt, }, utils::TargetFeatureIsSafeInTarget, }; -struct Coerce<'a, 'b, 'db> { - table: &'a mut InferenceTable<'db>, - has_errors: &'a mut bool, - target_features: &'a mut dyn FnMut() -> (&'b TargetFeatures, TargetFeatureIsSafeInTarget), +trait CoerceDelegate<'db> { + fn infcx(&self) -> &InferCtxt<'db>; + fn env(&self) -> &TraitEnvironment<'db>; + fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget); + + fn set_diverging(&mut self, diverging_ty: Ty<'db>); + + fn set_tainted_by_errors(&mut self); + + fn type_var_is_sized(&mut self, var: TyVid) -> bool; +} + +struct Coerce<D> { + delegate: D, use_lub: bool, /// Determines whether or not allow_two_phase_borrow is set on any /// autoref adjustments we create while coercing. We don't want to @@ -93,7 +105,7 @@ struct Coerce<'a, 'b, 'db> { cause: ObligationCause, } -type CoerceResult<'db> = InferResult<'db, (Vec<Adjustment>, Ty<'db>)>; +type CoerceResult<'db> = InferResult<'db, (Vec<Adjustment<'db>>, Ty<'db>)>; /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. @@ -103,53 +115,66 @@ fn coerce_mutbls<'db>(from_mutbl: Mutability, to_mutbl: Mutability) -> RelateRes /// This always returns `Ok(...)`. fn success<'db>( - adj: Vec<Adjustment>, + adj: Vec<Adjustment<'db>>, target: Ty<'db>, obligations: PredicateObligations<'db>, ) -> CoerceResult<'db> { Ok(InferOk { value: (adj, target), obligations }) } -impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { +impl<'db, D> Coerce<D> +where + D: CoerceDelegate<'db>, +{ #[inline] fn set_tainted_by_errors(&mut self) { - *self.has_errors = true; + self.delegate.set_tainted_by_errors(); + } + + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + self.delegate.infcx() + } + + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + self.delegate.env() } #[inline] fn interner(&self) -> DbInterner<'db> { - self.table.interner + self.infcx().interner } #[inline] - fn infer_ctxt(&self) -> &InferCtxt<'db> { - &self.table.infer_ctxt + fn db(&self) -> &'db dyn HirDatabase { + self.interner().db } pub(crate) fn commit_if_ok<T, E>( &mut self, f: impl FnOnce(&mut Self) -> Result<T, E>, ) -> Result<T, E> { - let snapshot = self.table.snapshot(); + let snapshot = self.infcx().start_snapshot(); let result = f(self); match result { Ok(_) => {} Err(_) => { - self.table.rollback_to(snapshot); + self.infcx().rollback_to(snapshot); } } result } - fn unify_raw(&mut self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> { + fn unify_raw(&self, a: Ty<'db>, b: Ty<'db>) -> InferResult<'db, Ty<'db>> { debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); - self.commit_if_ok(|this| { - let at = this.infer_ctxt().at(&this.cause, this.table.param_env); + self.infcx().commit_if_ok(|_| { + let at = self.infcx().at(&self.cause, self.env().env); - let res = if this.use_lub { + let res = if self.use_lub { at.lub(b, a) } else { - at.sup(DefineOpaqueTypes::Yes, b, a) + at.sup(b, a) .map(|InferOk { value: (), obligations }| InferOk { value: b, obligations }) }; @@ -158,9 +183,9 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { // Filter these cases out to make sure our coercion is more accurate. match res { Ok(InferOk { value, obligations }) => { - let mut ocx = ObligationCtxt::new(this.infer_ctxt()); + let mut ocx = ObligationCtxt::new(self.infcx()); ocx.register_obligations(obligations); - if ocx.select_where_possible().is_empty() { + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { Err(TypeError::Mismatch) @@ -182,17 +207,14 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { &mut self, a: Ty<'db>, b: Ty<'db>, - adjustments: impl IntoIterator<Item = Adjustment>, + adjustments: impl IntoIterator<Item = Adjustment<'db>>, final_adjustment: Adjust, ) -> CoerceResult<'db> { self.unify_raw(a, b).and_then(|InferOk { value: ty, obligations }| { success( adjustments .into_iter() - .chain(std::iter::once(Adjustment { - target: ty.to_chalk(self.interner()), - kind: final_adjustment, - })) + .chain(std::iter::once(Adjustment { target: ty, kind: final_adjustment })) .collect(), ty, obligations, @@ -203,24 +225,20 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { #[instrument(skip(self))] fn coerce(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { // First, remove any resolved type variables (at the top level, at least): - let a = self.table.shallow_resolve(a); - let b = self.table.shallow_resolve(b); + let a = self.infcx().shallow_resolve(a); + let b = self.infcx().shallow_resolve(b); debug!("Coerce.tys({:?} => {:?})", a, b); // Coercing from `!` to any type is allowed: if a.is_never() { // If we're coercing into an inference var, mark it as possibly diverging. - // FIXME: rustc does this differently. - if let TyKind::Infer(rustc_type_ir::TyVar(b)) = b.kind() { - self.table.set_diverging(b.as_u32().into(), chalk_ir::TyVariableKind::General); + if b.is_infer() { + self.delegate.set_diverging(b); } if self.coerce_never { return success( - vec![Adjustment { - kind: Adjust::NeverToAny, - target: b.to_chalk(self.interner()), - }], + vec![Adjustment { kind: Adjust::NeverToAny, target: b }], b, PredicateObligations::new(), ); @@ -230,25 +248,6 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { } } - // If we are coercing into a TAIT, coerce into its proxy inference var, instead. - // FIXME(next-solver): This should not be here. This is not how rustc does thing, and it also not allows us - // to normalize opaques defined in our scopes. Instead, we should properly register - // `TypingMode::Analysis::defining_opaque_types_and_generators`, and rely on the solver to reveal - // them for us (we'll also need some global-like registry for the values, something we cannot - // really implement, therefore we can really support only RPITs and ITIAT or the new `#[define_opaque]` - // TAIT, not the old global TAIT). - let mut b = b; - if let Some(tait_table) = &self.table.tait_coercion_table - && let TyKind::Alias(rustc_type_ir::Opaque, opaque_ty) = b.kind() - && let SolverDefId::InternedOpaqueTyId(opaque_ty_id) = opaque_ty.def_id - && !matches!(a.kind(), TyKind::Infer(..) | TyKind::Alias(rustc_type_ir::Opaque, _)) - && let Some(ty) = tait_table.get(&opaque_ty_id.into()) - { - b = ty.to_nextsolver(self.interner()); - b = self.table.shallow_resolve(b); - } - let b = b; - // Coercing *from* an unresolved inference variable means that // we have no information about the source type. This will always // ultimately fall back to some form of subtyping. @@ -317,12 +316,12 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { /// fall back to subtyping (`unify_and`). fn coerce_from_inference_variable(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); - debug_assert!(a.is_infer() && self.table.shallow_resolve(a) == a); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(a.is_infer() && self.infcx().shallow_resolve(a) == a); + debug_assert!(self.infcx().shallow_resolve(b) == b); if b.is_infer() { // Two unresolved type variables: create a `Coerce` predicate. - let target_ty = if self.use_lub { self.table.next_ty_var() } else { b }; + let target_ty = if self.use_lub { self.infcx().next_ty_var() } else { b }; let mut obligations = PredicateObligations::with_capacity(2); for &source_ty in &[a, b] { @@ -330,7 +329,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { obligations.push(Obligation::new( self.interner(), self.cause.clone(), - self.table.param_env, + self.env().env, Binder::dummy(PredicateKind::Coerce(CoercePredicate { a: source_ty, b: target_ty, @@ -362,8 +361,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { mutbl_b: Mutability, ) -> CoerceResult<'db> { debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); - debug_assert!(self.table.shallow_resolve(a) == a); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(a) == a); + debug_assert!(self.infcx().shallow_resolve(b) == b); // If we have a parameter of type `&M T_a` and the value // provided is `expr`, we will be adding an implicit borrow, @@ -382,10 +381,10 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { let mut first_error = None; let mut r_borrow_var = None; - let mut autoderef = Autoderef::new(self.table, a); + let mut autoderef = Autoderef::new_with_tracking(self.infcx(), self.env(), a); let mut found = None; - while let Some((referent_ty, autoderefs)) = autoderef.next() { + for (referent_ty, autoderefs) in autoderef.by_ref() { if autoderefs == 0 { // Don't let this pass, otherwise it would cause // &T to autoref to &&T. @@ -469,28 +468,18 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { } else { if r_borrow_var.is_none() { // create var lazily, at most once - let r = autoderef.table.next_region_var(); + let r = self.infcx().next_region_var(); r_borrow_var = Some(r); // [4] above } r_borrow_var.unwrap() }; let derefd_ty_a = Ty::new_ref( - autoderef.table.interner, + self.interner(), r, referent_ty, mutbl_b, // [1] above ); - // We need to construct a new `Coerce` because of lifetimes. - let mut coerce = Coerce { - table: autoderef.table, - has_errors: self.has_errors, - target_features: self.target_features, - use_lub: self.use_lub, - allow_two_phase: self.allow_two_phase, - coerce_never: self.coerce_never, - cause: self.cause.clone(), - }; - match coerce.unify_raw(derefd_ty_a, b) { + match self.unify_raw(derefd_ty_a, b) { Ok(ok) => { found = Some(ok); break; @@ -542,18 +531,9 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { autoderef.adjust_steps_as_infer_ok(); obligations.extend(o); - // Now apply the autoref. We have to extract the region out of - // the final ref type we got. - let TyKind::Ref(region, _, _) = ty.kind() else { - panic!("expected a ref type, got {:?}", ty); - }; - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref( - region.to_chalk(self.interner()), - mutbl_b.to_chalk(self.interner()), - )), - target: ty.to_chalk(self.interner()), - }); + // Now apply the autoref. + let mutbl = AutoBorrowMutability::new(mutbl_b, self.allow_two_phase); + adjustments.push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: ty }); debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); @@ -568,8 +548,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { #[instrument(skip(self), level = "debug")] fn coerce_unsized(&mut self, source: Ty<'db>, target: Ty<'db>) -> CoerceResult<'db> { debug!(?source, ?target); - debug_assert!(self.table.shallow_resolve(source) == source); - debug_assert!(self.table.shallow_resolve(target) == target); + debug_assert!(self.infcx().shallow_resolve(source) == source); + debug_assert!(self.infcx().shallow_resolve(target) == target); // We don't apply any coercions incase either the source or target // aren't sufficiently well known but tend to instead just equate @@ -631,10 +611,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { return Err(TypeError::Mismatch); } - let traits = ( - LangItem::Unsize.resolve_trait(self.table.db, self.table.trait_env.krate), - LangItem::CoerceUnsized.resolve_trait(self.table.db, self.table.trait_env.krate), - ); + let lang_items = self.interner().lang_items(); + let traits = (lang_items.Unsize, lang_items.CoerceUnsized); let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { debug!("missing Unsize or CoerceUnsized traits"); return Err(TypeError::Mismatch); @@ -650,26 +628,18 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { (TyKind::Ref(_, ty_a, mutbl_a), TyKind::Ref(_, _, mutbl_b)) => { coerce_mutbls(mutbl_a, mutbl_b)?; - let r_borrow = self.table.next_region_var(); + let r_borrow = self.infcx().next_region_var(); // We don't allow two-phase borrows here, at least for initial // implementation. If it happens that this coercion is a function argument, // the reborrow in coerce_borrowed_ptr will pick it up. - // let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); - let mutbl = mutbl_b.to_chalk(self.interner()); + let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Deref(None), - target: ty_a.to_chalk(self.interner()), - }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref( - r_borrow.to_chalk(self.interner()), - mutbl, - )), - target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b) - .to_chalk(self.interner()), + kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), + target: Ty::new_ref(self.interner(), r_borrow, ty_a, mutbl_b), }, )) } @@ -677,26 +647,22 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { coerce_mutbls(mt_a, mt_b)?; Some(( + Adjustment { kind: Adjust::Deref(None), target: ty_a }, Adjustment { - kind: Adjust::Deref(None), - target: ty_a.to_chalk(self.interner()), - }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b.to_chalk(self.interner()))), - target: Ty::new_ptr(self.interner(), ty_a, mt_b).to_chalk(self.interner()), + kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), + target: Ty::new_ptr(self.interner(), ty_a, mt_b), }, )) } _ => None, }; - let coerce_source = - reborrow.as_ref().map_or(source, |(_, r)| r.target.to_nextsolver(self.interner())); + let coerce_source = reborrow.as_ref().map_or(source, |(_, r)| r.target); // Setup either a subtyping or a LUB relationship between // the `CoerceUnsized` target type and the expected type. // We only have the latter, so we use an inference variable // for the former and let type inference do the rest. - let coerce_target = self.table.next_ty_var(); + let coerce_target = self.infcx().next_ty_var(); let mut coercion = self.unify_and( coerce_target, @@ -718,7 +684,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { let mut queue: SmallVec<[PredicateObligation<'db>; 4]> = smallvec![Obligation::new( self.interner(), cause, - self.table.param_env, + self.env().env, TraitRef::new( self.interner(), coerce_unsized_did.into(), @@ -735,16 +701,16 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { Some(PredicateKind::Clause(ClauseKind::Trait(trait_pred))) if traits.contains(&trait_pred.def_id().0) => { - self.infer_ctxt().resolve_vars_if_possible(trait_pred) + self.infcx().resolve_vars_if_possible(trait_pred) } // Eagerly process alias-relate obligations in new trait solver, // since these can be emitted in the process of solving trait goals, // but we need to constrain vars before processing goals mentioning // them. Some(PredicateKind::AliasRelate(..)) => { - let mut ocx = ObligationCtxt::new(self.infer_ctxt()); + let mut ocx = ObligationCtxt::new(self.infcx()); ocx.register_obligation(obligation); - if !ocx.select_where_possible().is_empty() { + if !ocx.try_evaluate_obligations().is_empty() { return Err(TypeError::Mismatch); } coercion.obligations.extend(ocx.into_pending_obligations()); @@ -756,7 +722,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { } }; debug!("coerce_unsized resolve step: {:?}", trait_pred); - match self.infer_ctxt().select(&obligation.with(self.interner(), trait_pred)) { + match self.infcx().select(&obligation.with(self.interner(), trait_pred)) { // Uncertain or unimplemented. Ok(None) => { if trait_pred.def_id().0 == unsize_did { @@ -765,7 +731,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); match (self_ty.kind(), unsize_ty.kind()) { (TyKind::Infer(rustc_type_ir::TyVar(v)), TyKind::Dynamic(..)) - if self.table.type_var_is_sized(v) => + if self.delegate.type_var_is_sized(v) => { debug!("coerce_unsized: have sized infer {:?}", v); coercion.obligations.push(obligation); @@ -837,7 +803,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { b: Ty<'db>, adjustment: Option<Adjust>, ) -> CoerceResult<'db> { - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(b) == b); self.commit_if_ok(|this| { if let TyKind::FnPtr(_, hdr_b) = b.kind() @@ -850,7 +816,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { b, adjustment.map(|kind| Adjustment { kind, - target: Ty::new_fn_ptr(this.interner(), fn_ty_a).to_chalk(this.interner()), + target: Ty::new_fn_ptr(this.interner(), fn_ty_a), }), Adjust::Pointer(PointerCast::UnsafeFnPointer), ) @@ -866,15 +832,15 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { fn coerce_from_fn_pointer(&mut self, fn_ty_a: PolyFnSig<'db>, b: Ty<'db>) -> CoerceResult<'db> { debug!(?fn_ty_a, ?b, "coerce_from_fn_pointer"); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(b) == b); self.coerce_from_safe_fn(fn_ty_a, b, None) } fn coerce_from_fn_item(&mut self, a: Ty<'db>, b: Ty<'db>) -> CoerceResult<'db> { debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); - debug_assert!(self.table.shallow_resolve(a) == a); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(a) == a); + debug_assert!(self.infcx().shallow_resolve(b) == b); match b.kind() { TyKind::FnPtr(_, b_hdr) => { @@ -882,22 +848,22 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { if let TyKind::FnDef(def_id, _) = a.kind() { // Intrinsics are not coercible to function pointers if let CallableDefId::FunctionId(def_id) = def_id.0 { - if FunctionSignature::is_intrinsic(self.table.db, def_id) { + if FunctionSignature::is_intrinsic(self.db(), def_id) { return Err(TypeError::IntrinsicCast); } - let attrs = self.table.db.attrs(def_id.into()); - if attrs.by_key(sym::rustc_force_inline).exists() { + let attrs = AttrFlags::query(self.db(), def_id.into()); + if attrs.contains(AttrFlags::RUSTC_FORCE_INLINE) { return Err(TypeError::ForceInlineCast); } - if b_hdr.safety.is_safe() && attrs.by_key(sym::target_feature).exists() { + if b_hdr.safety.is_safe() && attrs.contains(AttrFlags::HAS_TARGET_FEATURE) { let fn_target_features = - TargetFeatures::from_attrs_no_implications(&attrs); + TargetFeatures::from_fn_no_implications(self.db(), def_id); // Allow the coercion if the current function has all the features that would be // needed to call the coercee safely. let (target_features, target_feature_is_safe) = - (self.target_features)(); + self.delegate.target_features(); if target_feature_is_safe == TargetFeatureIsSafeInTarget::No && !target_features.enabled.is_superset(&fn_target_features.enabled) { @@ -928,8 +894,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { args_a: GenericArgs<'db>, b: Ty<'db>, ) -> CoerceResult<'db> { - debug_assert!(self.table.shallow_resolve(a) == a); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(a) == a); + debug_assert!(self.infcx().shallow_resolve(b) == b); match b.kind() { // FIXME: We need to have an `upvars_mentioned()` query: @@ -962,9 +928,7 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { pointer_ty, b, [], - Adjust::Pointer(PointerCast::ClosureFnPointer( - safety.to_chalk(self.interner()), - )), + Adjust::Pointer(PointerCast::ClosureFnPointer(safety)), ) } _ => self.unify(a, b), @@ -973,8 +937,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { fn coerce_raw_ptr(&mut self, a: Ty<'db>, b: Ty<'db>, mutbl_b: Mutability) -> CoerceResult<'db> { debug!("coerce_raw_ptr(a={:?}, b={:?})", a, b); - debug_assert!(self.table.shallow_resolve(a) == a); - debug_assert!(self.table.shallow_resolve(b) == b); + debug_assert!(self.infcx().shallow_resolve(a) == a); + debug_assert!(self.infcx().shallow_resolve(b) == b); let (is_ref, mt_a) = match a.kind() { TyKind::Ref(_, ty, mutbl) => (true, TypeAndMut::<DbInterner<'db>> { ty, mutbl }), @@ -992,11 +956,8 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { self.unify_and( a_raw, b, - [Adjustment { - kind: Adjust::Deref(None), - target: mt_a.ty.to_chalk(self.interner()), - }], - Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b.to_chalk(self.interner()))), + [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ) } else if mt_a.mutbl != mutbl_b { self.unify_and(a_raw, b, [], Adjust::Pointer(PointerCast::MutToConstPointer)) @@ -1006,13 +967,40 @@ impl<'a, 'b, 'db> Coerce<'a, 'b, 'db> { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum CoerceNever { - No, - Yes, +struct InferenceCoercionDelegate<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>); + +impl<'db> CoerceDelegate<'db> for InferenceCoercionDelegate<'_, '_, 'db> { + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + &self.0.table.infer_ctxt + } + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + &self.0.table.trait_env + } + + #[inline] + fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { + self.0.target_features() + } + + #[inline] + fn set_diverging(&mut self, diverging_ty: Ty<'db>) { + self.0.table.set_diverging(diverging_ty); + } + + #[inline] + fn set_tainted_by_errors(&mut self) { + self.0.set_tainted_by_errors(); + } + + #[inline] + fn type_var_is_sized(&mut self, var: TyVid) -> bool { + self.0.table.type_var_is_sized(var) + } } -impl<'db> InferenceContext<'db> { +impl<'db> InferenceContext<'_, 'db> { /// Attempt to coerce an expression to a type, and return the /// adjusted type of the expression, if successful. /// Adjustments are only recorded if the coercion succeeded. @@ -1023,24 +1011,26 @@ impl<'db> InferenceContext<'db> { expr_ty: Ty<'db>, mut target: Ty<'db>, allow_two_phase: AllowTwoPhase, - coerce_never: CoerceNever, + expr_is_read: ExprIsRead, ) -> RelateResult<'db, Ty<'db>> { let source = self.table.try_structurally_resolve_type(expr_ty); target = self.table.try_structurally_resolve_type(target); debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); let cause = ObligationCause::new(); - let krate = self.krate(); + let coerce_never = match expr { + ExprOrPatId::ExprId(idx) => { + self.expr_guaranteed_to_constitute_read_for_never(idx, expr_is_read) + } + // `PatId` is passed for `PatKind::Path`. + ExprOrPatId::PatId(_) => false, + }; let mut coerce = Coerce { - table: &mut self.table, - has_errors: &mut self.result.has_errors, + delegate: InferenceCoercionDelegate(self), cause, allow_two_phase, - coerce_never: matches!(coerce_never, CoerceNever::Yes), + coerce_never, use_lub: false, - target_features: &mut || { - Self::target_features(self.db, &self.target_features, self.owner, krate) - }, }; let ok = coerce.commit_if_ok(|coerce| coerce.coerce(source, target))?; @@ -1083,7 +1073,7 @@ impl<'db> InferenceContext<'db> { let is_force_inline = |ty: Ty<'db>| { if let TyKind::FnDef(CallableIdWrapper(CallableDefId::FunctionId(did)), _) = ty.kind() { - self.db.attrs(did.into()).by_key(sym::rustc_force_inline).exists() + AttrFlags::query(self.db, did.into()).contains(AttrFlags::RUSTC_FORCE_INLINE) } else { false } @@ -1114,9 +1104,13 @@ impl<'db> InferenceContext<'db> { match self.table.commit_if_ok(|table| { // We need to eagerly handle nested obligations due to lazy norm. let mut ocx = ObligationCtxt::new(&table.infer_ctxt); - let value = - ocx.lub(&ObligationCause::new(), table.param_env, prev_ty, new_ty)?; - if ocx.select_where_possible().is_empty() { + let value = ocx.lub( + &ObligationCause::new(), + table.trait_env.env, + prev_ty, + new_ty, + )?; + if ocx.try_evaluate_obligations().is_empty() { Ok(InferOk { value, obligations: ocx.into_pending_obligations() }) } else { Err(TypeError::Mismatch) @@ -1125,13 +1119,13 @@ impl<'db> InferenceContext<'db> { // We have a LUB of prev_ty and new_ty, just return it. Ok(ok) => return Ok(self.table.register_infer_ok(ok)), Err(_) => ( - Some(prev_ty.fn_sig(self.table.interner)), - Some(new_ty.fn_sig(self.table.interner)), + Some(prev_ty.fn_sig(self.table.interner())), + Some(new_ty.fn_sig(self.table.interner())), ), } } (TyKind::Closure(_, args), TyKind::FnDef(..)) => { - let b_sig = new_ty.fn_sig(self.table.interner); + let b_sig = new_ty.fn_sig(self.table.interner()); let a_sig = args.closure_sig_untupled().map_bound(|mut sig| { sig.safety = b_sig.safety(); sig @@ -1139,7 +1133,7 @@ impl<'db> InferenceContext<'db> { (Some(a_sig), Some(b_sig)) } (TyKind::FnDef(..), TyKind::Closure(_, args)) => { - let a_sig = prev_ty.fn_sig(self.table.interner); + let a_sig = prev_ty.fn_sig(self.table.interner()); let b_sig = args.closure_sig_untupled().map_bound(|mut sig| { sig.safety = a_sig.safety(); sig @@ -1158,41 +1152,35 @@ impl<'db> InferenceContext<'db> { let sig = self .table .infer_ctxt - .at(&ObligationCause::new(), self.table.param_env) + .at(&ObligationCause::new(), self.table.trait_env.env) .lub(a_sig, b_sig) .map(|ok| self.table.register_infer_ok(ok))?; // Reify both sides and return the reified fn pointer type. - let fn_ptr = Ty::new_fn_ptr(self.table.interner, sig); + let fn_ptr = Ty::new_fn_ptr(self.table.interner(), sig); let prev_adjustment = match prev_ty.kind() { - TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( - a_sig.safety().to_chalk(self.table.interner), - )), + TyKind::Closure(..) => { + Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.safety())) + } TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => panic!("should not try to coerce a {prev_ty:?} to a fn pointer"), }; let next_adjustment = match new_ty.kind() { - TyKind::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer( - b_sig.safety().to_chalk(self.table.interner), - )), + TyKind::Closure(..) => { + Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.safety())) + } TyKind::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), _ => panic!("should not try to coerce a {new_ty:?} to a fn pointer"), }; for &expr in exprs { self.write_expr_adj( expr, - Box::new([Adjustment { - kind: prev_adjustment.clone(), - target: fn_ptr.to_chalk(self.table.interner), - }]), + Box::new([Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }]), ); } self.write_expr_adj( new, - Box::new([Adjustment { - kind: next_adjustment, - target: fn_ptr.to_chalk(self.table.interner), - }]), + Box::new([Adjustment { kind: next_adjustment, target: fn_ptr }]), ); return Ok(fn_ptr); } @@ -1205,23 +1193,18 @@ impl<'db> InferenceContext<'db> { // // NOTE: we set `coerce_never` to `true` here because coercion LUBs only // operate on values and not places, so a never coercion is valid. - let krate = self.krate(); let mut coerce = Coerce { - table: &mut self.table, - has_errors: &mut self.result.has_errors, + delegate: InferenceCoercionDelegate(self), cause: ObligationCause::new(), allow_two_phase: AllowTwoPhase::No, coerce_never: true, use_lub: true, - target_features: &mut || { - Self::target_features(self.db, &self.target_features, self.owner, krate) - }, }; // First try to coerce the new expression to the type of the previous ones, // but only if the new expression has no coercion already applied to it. let mut first_error = None; - if !self.result.expr_adjustments.contains_key(&new) { + if !coerce.delegate.0.result.expr_adjustments.contains_key(&new) { let result = coerce.commit_if_ok(|coerce| coerce.coerce(new_ty, prev_ty)); match result { Ok(ok) => { @@ -1248,7 +1231,7 @@ impl<'db> InferenceContext<'db> { .commit_if_ok(|table| { table .infer_ctxt - .at(&ObligationCause::new(), table.param_env) + .at(&ObligationCause::new(), table.trait_env.env) .lub(prev_ty, new_ty) }) .unwrap_err()) @@ -1379,12 +1362,13 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// if necessary. pub(crate) fn coerce( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, cause: &ObligationCause, expression: ExprId, expression_ty: Ty<'db>, + expr_is_read: ExprIsRead, ) { - self.coerce_inner(icx, cause, expression, expression_ty, false, false) + self.coerce_inner(icx, cause, expression, expression_ty, false, false, expr_is_read) } /// Indicates that one of the inputs is a "forced unit". This @@ -1401,18 +1385,20 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// removing a `;`). pub(crate) fn coerce_forced_unit( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, expr: ExprId, cause: &ObligationCause, label_unit_as_expected: bool, + expr_is_read: ExprIsRead, ) { self.coerce_inner( icx, cause, expr, - icx.result.standard_types.unit.to_nextsolver(icx.table.interner), + icx.types.unit, true, label_unit_as_expected, + expr_is_read, ) } @@ -1421,12 +1407,13 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { /// `Nil`. pub(crate) fn coerce_inner( &mut self, - icx: &mut InferenceContext<'db>, + icx: &mut InferenceContext<'_, 'db>, cause: &ObligationCause, expression: ExprId, mut expression_ty: Ty<'db>, force_unit: bool, label_expression_as_expected: bool, + expr_is_read: ExprIsRead, ) { // Incorporate whatever type inference information we have // until now; in principle we might also want to process @@ -1463,7 +1450,7 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { expression_ty, self.expected_ty, AllowTwoPhase::No, - CoerceNever::Yes, + expr_is_read, ) } else { match self.expressions { @@ -1496,19 +1483,12 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // // Another example is `break` with no argument expression. assert!(expression_ty.is_unit(), "if let hack without unit type"); - icx.table - .infer_ctxt - .at(cause, icx.table.param_env) - .eq( - // needed for tests/ui/type-alias-impl-trait/issue-65679-inst-opaque-ty-from-val-twice.rs - DefineOpaqueTypes::Yes, - expected, - found, - ) - .map(|infer_ok| { + icx.table.infer_ctxt.at(cause, icx.table.trait_env.env).eq(expected, found).map( + |infer_ok| { icx.table.register_infer_ok(infer_ok); expression_ty - }) + }, + ) }; debug!(?result); @@ -1530,20 +1510,14 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { // emit or provide suggestions on how to fix the initial error. icx.set_tainted_by_errors(); - self.final_ty = Some(Ty::new_error(icx.table.interner, ErrorGuaranteed)); + self.final_ty = Some(icx.types.error); - icx.result.type_mismatches.insert( + icx.result.type_mismatches.get_or_insert_default().insert( expression.into(), if label_expression_as_expected { - TypeMismatch { - expected: found.to_chalk(icx.table.interner), - actual: expected.to_chalk(icx.table.interner), - } + TypeMismatch { expected: found, actual: expected } } else { - TypeMismatch { - expected: expected.to_chalk(icx.table.interner), - actual: found.to_chalk(icx.table.interner), - } + TypeMismatch { expected, actual: found } }, ); } @@ -1552,73 +1526,190 @@ impl<'db, 'exprs> CoerceMany<'db, 'exprs> { self.pushed += 1; } - pub(crate) fn complete(self, icx: &mut InferenceContext<'db>) -> Ty<'db> { + pub(crate) fn complete(self, icx: &mut InferenceContext<'_, 'db>) -> Ty<'db> { if let Some(final_ty) = self.final_ty { final_ty } else { // If we only had inputs that were of type `!` (or no // inputs at all), then the final type is `!`. assert_eq!(self.pushed, 0); - icx.result.standard_types.never.to_nextsolver(icx.table.interner) + icx.types.never } } } -pub fn could_coerce( - db: &dyn HirDatabase, - env: Arc<TraitEnvironment>, - tys: &crate::Canonical<(crate::Ty, crate::Ty)>, +pub fn could_coerce<'db>( + db: &'db dyn HirDatabase, + env: Arc<TraitEnvironment<'db>>, + tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>, ) -> bool { coerce(db, env, tys).is_ok() } +struct HirCoercionDelegate<'a, 'db> { + infcx: &'a InferCtxt<'db>, + env: &'a TraitEnvironment<'db>, + target_features: &'a TargetFeatures<'db>, +} + +impl<'db> CoerceDelegate<'db> for HirCoercionDelegate<'_, 'db> { + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + self.infcx + } + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + self.env + } + fn target_features(&self) -> (&TargetFeatures<'db>, TargetFeatureIsSafeInTarget) { + (self.target_features, TargetFeatureIsSafeInTarget::No) + } + fn set_diverging(&mut self, _diverging_ty: Ty<'db>) {} + fn set_tainted_by_errors(&mut self) {} + fn type_var_is_sized(&mut self, _var: TyVid) -> bool { + false + } +} + fn coerce<'db>( db: &'db dyn HirDatabase, - env: Arc<TraitEnvironment>, - tys: &crate::Canonical<(crate::Ty, crate::Ty)>, -) -> Result<(Vec<Adjustment>, crate::Ty), TypeError<DbInterner<'db>>> { - let mut table = InferenceTable::new(db, env); - let vars = table.fresh_subst(tys.binders.as_slice(Interner)); - let ty1_with_vars = vars.apply(tys.value.0.clone(), Interner); - let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); + env: Arc<TraitEnvironment<'db>>, + tys: &Canonical<'db, (Ty<'db>, Ty<'db>)>, +) -> Result<(Vec<Adjustment<'db>>, Ty<'db>), TypeError<DbInterner<'db>>> { + let interner = DbInterner::new_with(db, env.krate); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let ((ty1_with_vars, ty2_with_vars), vars) = infcx.instantiate_canonical(tys); let cause = ObligationCause::new(); // FIXME: Target features. let target_features = TargetFeatures::default(); let mut coerce = Coerce { - table: &mut table, - has_errors: &mut false, + delegate: HirCoercionDelegate { + infcx: &infcx, + env: &env, + target_features: &target_features, + }, cause, allow_two_phase: AllowTwoPhase::No, coerce_never: true, use_lub: false, - target_features: &mut || (&target_features, TargetFeatureIsSafeInTarget::No), }; - let InferOk { value: (adjustments, ty), obligations } = coerce.coerce( - ty1_with_vars.to_nextsolver(coerce.table.interner), - ty2_with_vars.to_nextsolver(coerce.table.interner), - )?; - table.register_predicates(obligations); + let infer_ok = coerce.coerce(ty1_with_vars, ty2_with_vars)?; + let mut ocx = ObligationCtxt::new(&infcx); + let (adjustments, ty) = ocx.register_infer_ok_obligations(infer_ok); + _ = ocx.try_evaluate_obligations(); + let (adjustments, ty) = infcx.resolve_vars_if_possible((adjustments, ty)); // default any type vars that weren't unified back to their original bound vars // (kind of hacky) - let find_var = |iv| { - vars.iter(Interner).position(|v| match v.interned() { - chalk_ir::GenericArgData::Ty(ty) => ty.inference_var(Interner), - chalk_ir::GenericArgData::Lifetime(lt) => lt.inference_var(Interner), - chalk_ir::GenericArgData::Const(c) => c.inference_var(Interner), - } == Some(iv)) - }; - let fallback = |iv, kind, default, binder| match kind { - chalk_ir::VariableKind::Ty(_ty_kind) => find_var(iv) - .map_or(default, |i| crate::BoundVar::new(binder, i).to_ty(Interner).cast(Interner)), - chalk_ir::VariableKind::Lifetime => find_var(iv).map_or(default, |i| { - crate::BoundVar::new(binder, i).to_lifetime(Interner).cast(Interner) - }), - chalk_ir::VariableKind::Const(ty) => find_var(iv).map_or(default, |i| { - crate::BoundVar::new(binder, i).to_const(Interner, ty).cast(Interner) - }), - }; - // FIXME also map the types in the adjustments - Ok((adjustments, table.resolve_with_fallback(ty.to_chalk(table.interner), &fallback))) + + struct Resolver<'db> { + interner: DbInterner<'db>, + debruijn: DebruijnIndex, + var_values: GenericArgs<'db>, + } + + impl<'db> TypeFolder<DbInterner<'db>> for Resolver<'db> { + fn cx(&self) -> DbInterner<'db> { + self.interner + } + + fn fold_binder<T>(&mut self, t: Binder<'db, T>) -> Binder<'db, T> + where + T: TypeFoldable<DbInterner<'db>>, + { + self.debruijn.shift_in(1); + let result = t.super_fold_with(self); + self.debruijn.shift_out(1); + result + } + + fn fold_ty(&mut self, t: Ty<'db>) -> Ty<'db> { + if !t.has_infer() { + return t; + } + + if let TyKind::Infer(infer) = t.kind() { + let var = self.var_values.iter().position(|arg| { + arg.as_type().is_some_and(|ty| match ty.kind() { + TyKind::Infer(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Ty::new_error(self.interner, ErrorGuaranteed), + |i| { + Ty::new_bound( + self.interner, + self.debruijn, + BoundTy { kind: BoundTyKind::Anon, var: BoundVar::from_usize(i) }, + ) + }, + ) + } else { + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, c: Const<'db>) -> Const<'db> { + if !c.has_infer() { + return c; + } + + if let ConstKind::Infer(infer) = c.kind() { + let var = self.var_values.iter().position(|arg| { + arg.as_const().is_some_and(|ty| match ty.kind() { + ConstKind::Infer(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Const::new_error(self.interner, ErrorGuaranteed), + |i| { + Const::new_bound( + self.interner, + self.debruijn, + BoundConst { var: BoundVar::from_usize(i) }, + ) + }, + ) + } else { + c.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: Region<'db>) -> Region<'db> { + if let RegionKind::ReVar(infer) = r.kind() { + let var = self.var_values.iter().position(|arg| { + arg.as_region().is_some_and(|ty| match ty.kind() { + RegionKind::ReVar(it) => infer == it, + _ => false, + }) + }); + var.map_or_else( + || Region::error(self.interner), + |i| { + Region::new_bound( + self.interner, + self.debruijn, + BoundRegion { + kind: BoundRegionKind::Anon, + var: BoundVar::from_usize(i), + }, + ) + }, + ) + } else { + r + } + } + } + + // FIXME: We don't fallback correctly since this is done on `InferenceContext` and we only have `InferCtxt`. + let (adjustments, ty) = (adjustments, ty).fold_with(&mut Resolver { + interner, + debruijn: DebruijnIndex::ZERO, + var_values: vars.var_values, + }); + Ok((adjustments, ty)) } |