Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/autoderef.rs')
| -rw-r--r-- | crates/hir-ty/src/autoderef.rs | 221 |
1 files changed, 150 insertions, 71 deletions
diff --git a/crates/hir-ty/src/autoderef.rs b/crates/hir-ty/src/autoderef.rs index 392b0b0408..d21108fb5a 100644 --- a/crates/hir-ty/src/autoderef.rs +++ b/crates/hir-ty/src/autoderef.rs @@ -13,11 +13,11 @@ use triomphe::Arc; use crate::{ TraitEnvironment, db::HirDatabase, - infer::unify::InferenceTable, + infer::InferenceContext, next_solver::{ - Canonical, TraitRef, Ty, TyKind, + Canonical, DbInterner, ParamEnv, TraitRef, Ty, TyKind, TypingMode, infer::{ - InferOk, + DbInternerInferExt, InferCtxt, traits::{Obligation, ObligationCause, PredicateObligations}, }, obligation_ctxt::ObligationCtxt, @@ -38,14 +38,15 @@ pub fn autoderef<'db>( env: Arc<TraitEnvironment<'db>>, ty: Canonical<'db, Ty<'db>>, ) -> impl Iterator<Item = Ty<'db>> + use<'db> { - let mut table = InferenceTable::new(db, env, None); - let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty); + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis); + let (ty, _) = infcx.instantiate_canonical(&ty); + let autoderef = Autoderef::new(&infcx, &env, ty); let mut v = Vec::new(); - while let Some((ty, _steps)) = autoderef.next() { + for (ty, _steps) in autoderef { // `ty` may contain unresolved inference variables. Since there's no chance they would be // resolved, just replace with fallback type. - let resolved = autoderef.table.resolve_completely(ty); + let resolved = infcx.resolve_vars_if_possible(ty).replace_infer_with_error(interner); // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. @@ -105,13 +106,48 @@ struct AutoderefTraits { trait_target: TypeAliasId, } +// We use a trait here and a generic implementation unfortunately, because sometimes (specifically +// in place_op.rs), you need to have mutable access to the `InferenceContext` while the `Autoderef` +// borrows it. +pub(crate) trait AutoderefCtx<'db> { + fn infcx(&self) -> &InferCtxt<'db>; + fn env(&self) -> &TraitEnvironment<'db>; +} + +pub(crate) struct DefaultAutoderefCtx<'a, 'db> { + infcx: &'a InferCtxt<'db>, + env: &'a TraitEnvironment<'db>, +} +impl<'db> AutoderefCtx<'db> for DefaultAutoderefCtx<'_, 'db> { + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + self.infcx + } + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + self.env + } +} + +pub(crate) struct InferenceContextAutoderefCtx<'a, 'b, 'db>(&'a mut InferenceContext<'b, 'db>); +impl<'db> AutoderefCtx<'db> for InferenceContextAutoderefCtx<'_, '_, 'db> { + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + &self.0.table.infer_ctxt + } + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + &self.0.table.trait_env + } +} + /// Recursively dereference a type, considering both built-in /// dereferences (`*`) and the `Deref` trait. /// Although called `Autoderef` it can be configured to use the /// `Receiver` trait instead of the `Deref` trait. -pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> { +pub(crate) struct GeneralAutoderef<'db, Ctx, Steps = Vec<(Ty<'db>, AutoderefKind)>> { // Meta infos: - pub(crate) table: &'a mut InferenceTable<'db>, + ctx: Ctx, traits: Option<AutoderefTraits>, // Current state: @@ -122,7 +158,16 @@ pub(crate) struct Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> { use_receiver_trait: bool, } -impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, Steps> { +pub(crate) type Autoderef<'a, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> = + GeneralAutoderef<'db, DefaultAutoderefCtx<'a, 'db>, Steps>; +pub(crate) type InferenceContextAutoderef<'a, 'b, 'db, Steps = Vec<(Ty<'db>, AutoderefKind)>> = + GeneralAutoderef<'db, InferenceContextAutoderefCtx<'a, 'b, 'db>, Steps>; + +impl<'db, Ctx, Steps> Iterator for GeneralAutoderef<'db, Ctx, Steps> +where + Ctx: AutoderefCtx<'db>, + Steps: TrackAutoderefSteps<'db>, +{ type Item = (Ty<'db>, usize); fn next(&mut self) -> Option<Self::Item> { @@ -148,26 +193,26 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, S // be better to skip this clause and use the Overloaded case only, since &T // and &mut T implement Receiver. But built-in derefs apply equally to Receiver // and Deref, and this has benefits for const and the emitted MIR. - let (kind, new_ty) = if let Some(ty) = - self.state.cur_ty.builtin_deref(self.table.db, self.include_raw_pointers) - { - debug_assert_eq!(ty, self.table.infer_ctxt.resolve_vars_if_possible(ty)); - // NOTE: we may still need to normalize the built-in deref in case - // we have some type like `&<Ty as Trait>::Assoc`, since users of - // autoderef expect this type to have been structurally normalized. - if let TyKind::Alias(..) = ty.kind() { - let (normalized_ty, obligations) = structurally_normalize_ty(self.table, ty)?; - self.state.obligations.extend(obligations); - (AutoderefKind::Builtin, normalized_ty) + let (kind, new_ty) = + if let Some(ty) = self.state.cur_ty.builtin_deref(self.include_raw_pointers) { + debug_assert_eq!(ty, self.infcx().resolve_vars_if_possible(ty)); + // NOTE: we may still need to normalize the built-in deref in case + // we have some type like `&<Ty as Trait>::Assoc`, since users of + // autoderef expect this type to have been structurally normalized. + if let TyKind::Alias(..) = ty.kind() { + let (normalized_ty, obligations) = + structurally_normalize_ty(self.infcx(), self.env().env, ty)?; + self.state.obligations.extend(obligations); + (AutoderefKind::Builtin, normalized_ty) + } else { + (AutoderefKind::Builtin, ty) + } + } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { + // The overloaded deref check already normalizes the pointee type. + (AutoderefKind::Overloaded, ty) } else { - (AutoderefKind::Builtin, ty) - } - } else if let Some(ty) = self.overloaded_deref_ty(self.state.cur_ty) { - // The overloaded deref check already normalizes the pointee type. - (AutoderefKind::Overloaded, ty) - } else { - return None; - }; + return None; + }; self.state.steps.push(self.state.cur_ty, kind); debug!( @@ -183,34 +228,84 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Iterator for Autoderef<'a, 'db, S } impl<'a, 'db> Autoderef<'a, 'db> { - pub(crate) fn new(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { - Self::new_impl(table, base_ty) + #[inline] + pub(crate) fn new_with_tracking( + infcx: &'a InferCtxt<'db>, + env: &'a TraitEnvironment<'db>, + base_ty: Ty<'db>, + ) -> Self { + Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty) + } +} + +impl<'a, 'b, 'db> InferenceContextAutoderef<'a, 'b, 'db> { + #[inline] + pub(crate) fn new_from_inference_context( + ctx: &'a mut InferenceContext<'b, 'db>, + base_ty: Ty<'db>, + ) -> Self { + Self::new_impl(InferenceContextAutoderefCtx(ctx), base_ty) + } + + #[inline] + pub(crate) fn ctx(&mut self) -> &mut InferenceContext<'b, 'db> { + self.ctx.0 } } impl<'a, 'db> Autoderef<'a, 'db, usize> { - pub(crate) fn new_no_tracking(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { - Self::new_impl(table, base_ty) + #[inline] + pub(crate) fn new( + infcx: &'a InferCtxt<'db>, + env: &'a TraitEnvironment<'db>, + base_ty: Ty<'db>, + ) -> Self { + Self::new_impl(DefaultAutoderefCtx { infcx, env }, base_ty) } } -impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { - fn new_impl(table: &'a mut InferenceTable<'db>, base_ty: Ty<'db>) -> Self { - Autoderef { +impl<'db, Ctx, Steps> GeneralAutoderef<'db, Ctx, Steps> +where + Ctx: AutoderefCtx<'db>, + Steps: TrackAutoderefSteps<'db>, +{ + #[inline] + fn new_impl(ctx: Ctx, base_ty: Ty<'db>) -> Self { + GeneralAutoderef { state: AutoderefSnapshot { steps: Steps::default(), - cur_ty: table.infer_ctxt.resolve_vars_if_possible(base_ty), + cur_ty: ctx.infcx().resolve_vars_if_possible(base_ty), obligations: PredicateObligations::new(), at_start: true, reached_recursion_limit: false, }, - table, + ctx, traits: None, include_raw_pointers: false, use_receiver_trait: false, } } + #[inline] + fn infcx(&self) -> &InferCtxt<'db> { + self.ctx.infcx() + } + + #[inline] + fn env(&self) -> &TraitEnvironment<'db> { + self.ctx.env() + } + + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx().interner + } + + #[inline] + fn db(&self) -> &'db dyn HirDatabase { + self.interner().db + } + fn autoderef_traits(&mut self) -> Option<AutoderefTraits> { match &mut self.traits { Some(it) => Some(*it), @@ -219,25 +314,23 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { (|| { Some(AutoderefTraits { trait_: LangItem::Receiver - .resolve_trait(self.table.db, self.table.trait_env.krate)?, + .resolve_trait(self.db(), self.env().krate)?, trait_target: LangItem::ReceiverTarget - .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + .resolve_type_alias(self.db(), self.env().krate)?, }) })() .or_else(|| { Some(AutoderefTraits { - trait_: LangItem::Deref - .resolve_trait(self.table.db, self.table.trait_env.krate)?, + trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?, trait_target: LangItem::DerefTarget - .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + .resolve_type_alias(self.db(), self.env().krate)?, }) })? } else { AutoderefTraits { - trait_: LangItem::Deref - .resolve_trait(self.table.db, self.table.trait_env.krate)?, + trait_: LangItem::Deref.resolve_trait(self.db(), self.env().krate)?, trait_target: LangItem::DerefTarget - .resolve_type_alias(self.table.db, self.table.trait_env.krate)?, + .resolve_type_alias(self.db(), self.env().krate)?, } }; Some(*self.traits.insert(traits)) @@ -247,31 +340,32 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { fn overloaded_deref_ty(&mut self, ty: Ty<'db>) -> Option<Ty<'db>> { debug!("overloaded_deref_ty({:?})", ty); - let interner = self.table.interner(); + let interner = self.interner(); // <ty as Deref>, or whatever the equivalent trait is that we've been asked to walk. let AutoderefTraits { trait_, trait_target } = self.autoderef_traits()?; let trait_ref = TraitRef::new(interner, trait_.into(), [ty]); let obligation = - Obligation::new(interner, ObligationCause::new(), self.table.trait_env.env, trait_ref); + Obligation::new(interner, ObligationCause::new(), self.env().env, trait_ref); // We detect whether the self type implements `Deref` before trying to // structurally normalize. We use `predicate_may_hold_opaque_types_jank` // to support not-yet-defined opaque types. It will succeed for `impl Deref` // but fail for `impl OtherTrait`. - if !self.table.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation) { + if !self.infcx().predicate_may_hold_opaque_types_jank(&obligation) { debug!("overloaded_deref_ty: cannot match obligation"); return None; } let (normalized_ty, obligations) = structurally_normalize_ty( - self.table, + self.infcx(), + self.env().env, Ty::new_projection(interner, trait_target.into(), [ty]), )?; debug!("overloaded_deref_ty({:?}) = ({:?}, {:?})", ty, normalized_ty, obligations); self.state.obligations.extend(obligations); - Some(self.table.infer_ctxt.resolve_vars_if_possible(normalized_ty)) + Some(self.infcx().resolve_vars_if_possible(normalized_ty)) } /// Returns the final type we ended up with, which may be an unresolved @@ -292,7 +386,6 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { &self.state.steps } - #[expect(dead_code)] pub(crate) fn reached_recursion_limit(&self) -> bool { self.state.reached_recursion_limit } @@ -316,12 +409,12 @@ impl<'a, 'db, Steps: TrackAutoderefSteps<'db>> Autoderef<'a, 'db, Steps> { } fn structurally_normalize_ty<'db>( - table: &InferenceTable<'db>, + infcx: &InferCtxt<'db>, + param_env: ParamEnv<'db>, ty: Ty<'db>, ) -> Option<(Ty<'db>, PredicateObligations<'db>)> { - let mut ocx = ObligationCtxt::new(&table.infer_ctxt); - let Ok(normalized_ty) = - ocx.structurally_normalize_ty(&ObligationCause::misc(), table.trait_env.env, ty) + let mut ocx = ObligationCtxt::new(infcx); + let Ok(normalized_ty) = ocx.structurally_normalize_ty(&ObligationCause::misc(), param_env, ty) else { // We shouldn't have errors here in the old solver, except for // evaluate/fulfill mismatches, but that's not a reason for an ICE. @@ -334,17 +427,3 @@ fn structurally_normalize_ty<'db>( Some((normalized_ty, ocx.into_pending_obligations())) } - -pub(crate) fn overloaded_deref_ty<'db>( - table: &InferenceTable<'db>, - ty: Ty<'db>, -) -> Option<InferOk<'db, Ty<'db>>> { - let interner = table.interner(); - - let trait_target = LangItem::DerefTarget.resolve_type_alias(table.db, table.trait_env.krate)?; - - let (normalized_ty, obligations) = - structurally_normalize_ty(table, Ty::new_projection(interner, trait_target.into(), [ty]))?; - - Some(InferOk { value: normalized_ty, obligations }) -} |