Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/opaques.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/opaques.rs | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/crates/hir-ty/src/infer/opaques.rs b/crates/hir-ty/src/infer/opaques.rs new file mode 100644 index 0000000000..f7719f50ac --- /dev/null +++ b/crates/hir-ty/src/infer/opaques.rs @@ -0,0 +1,147 @@ +//! Defining opaque types via inference. + +use rustc_type_ir::{TypeVisitableExt, fold_regions}; +use tracing::{debug, instrument}; + +use crate::{ + infer::InferenceContext, + next_solver::{ + EarlyBinder, OpaqueTypeKey, SolverDefId, TypingMode, + infer::{opaque_types::OpaqueHiddenType, traits::ObligationCause}, + }, +}; + +impl<'db> InferenceContext<'_, 'db> { + /// This takes all the opaque type uses during HIR typeck. It first computes + /// the concrete hidden type by iterating over all defining uses. + /// + /// A use during HIR typeck is defining if all non-lifetime arguments are + /// unique generic parameters and the hidden type does not reference any + /// inference variables. + /// + /// It then uses these defining uses to guide inference for all other uses. + #[instrument(level = "debug", skip(self))] + pub(super) fn handle_opaque_type_uses(&mut self) { + // We clone the opaques instead of stealing them here as they are still used for + // normalization in the next generation trait solver. + let opaque_types: Vec<_> = self.table.infer_ctxt.clone_opaque_types(); + + self.compute_definition_site_hidden_types(opaque_types); + } +} + +#[expect(unused, reason = "rustc has this")] +#[derive(Copy, Clone, Debug)] +enum UsageKind<'db> { + None, + NonDefiningUse(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>), + UnconstrainedHiddenType(OpaqueHiddenType<'db>), + HasDefiningUse(OpaqueHiddenType<'db>), +} + +impl<'db> UsageKind<'db> { + fn merge(&mut self, other: UsageKind<'db>) { + match (&*self, &other) { + (UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(), + (UsageKind::None, _) => *self = other, + // When mergining non-defining uses, prefer earlier ones. This means + // the error happens as early as possible. + ( + UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..), + UsageKind::NonDefiningUse(..), + ) => {} + // When merging unconstrained hidden types, we prefer later ones. This is + // used as in most cases, the defining use is the final return statement + // of our function, and other uses with defining arguments are likely not + // intended to be defining. + ( + UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..), + UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_), + ) => *self = other, + } + } +} + +impl<'db> InferenceContext<'_, 'db> { + fn compute_definition_site_hidden_types( + &mut self, + mut opaque_types: Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)>, + ) { + for entry in opaque_types.iter_mut() { + *entry = self.table.infer_ctxt.resolve_vars_if_possible(*entry); + } + debug!(?opaque_types); + + let interner = self.interner(); + let TypingMode::Analysis { defining_opaque_types_and_generators } = + self.table.infer_ctxt.typing_mode() + else { + unreachable!(); + }; + + for def_id in defining_opaque_types_and_generators { + let def_id = match def_id { + SolverDefId::InternedOpaqueTyId(it) => it, + _ => continue, + }; + + // We do actually need to check this the second pass (we can't just + // store this), because we can go from `UnconstrainedHiddenType` to + // `HasDefiningUse` (because of fallback) + let mut usage_kind = UsageKind::None; + for &(opaque_type_key, hidden_type) in &opaque_types { + if opaque_type_key.def_id != def_id.into() { + continue; + } + + usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type)); + + if let UsageKind::HasDefiningUse(..) = usage_kind { + break; + } + } + + if let UsageKind::HasDefiningUse(ty) = usage_kind { + for &(opaque_type_key, hidden_type) in &opaque_types { + if opaque_type_key.def_id != def_id.into() { + continue; + } + + let expected = + EarlyBinder::bind(ty.ty).instantiate(interner, opaque_type_key.args); + self.demand_eqtype(expected, hidden_type.ty); + } + + self.result.type_of_opaque.insert(def_id, ty.ty); + + continue; + } + + self.result.type_of_opaque.insert(def_id, self.types.error); + } + } + + #[tracing::instrument(skip(self), ret)] + fn consider_opaque_type_use( + &self, + opaque_type_key: OpaqueTypeKey<'db>, + hidden_type: OpaqueHiddenType<'db>, + ) -> UsageKind<'db> { + // We ignore uses of the opaque if they have any inference variables + // as this can frequently happen with recursive calls. + // + // See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`. + if hidden_type.ty.has_non_region_infer() { + return UsageKind::UnconstrainedHiddenType(hidden_type); + } + + let cause = ObligationCause::new(); + let at = self.table.infer_ctxt.at(&cause, self.table.trait_env.env); + let hidden_type = match at.deeply_normalize(hidden_type) { + Ok(hidden_type) => hidden_type, + Err(_errors) => OpaqueHiddenType { ty: self.types.error }, + }; + let hidden_type = fold_regions(self.interner(), hidden_type, |_, _| self.types.re_erased); + UsageKind::HasDefiningUse(hidden_type) + } +} |