Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer.rs')
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 296 |
1 files changed, 35 insertions, 261 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 361e66522d..016edb2310 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -21,6 +21,7 @@ pub(crate) mod diagnostics; mod expr; mod fallback; mod mutability; +mod opaques; mod pat; mod path; pub(crate) mod unify; @@ -31,8 +32,7 @@ use base_db::Crate; use either::Either; use hir_def::{ AdtId, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, GenericDefId, GenericParamId, - ImplId, ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, - VariantId, + ItemContainerId, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, TypeAliasId, VariantId, expr_store::{Body, ExpressionStore, HygieneId, path::Path}, hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, PatId}, lang_item::{LangItem, LangItemTarget, lang_item}, @@ -44,11 +44,11 @@ use hir_def::{ use hir_expand::{mod_path::ModPath, name::Name}; use indexmap::IndexSet; use intern::sym; -use la_arena::{ArenaMap, Entry}; +use la_arena::ArenaMap; use rustc_ast_ir::Mutability; use rustc_hash::{FxHashMap, FxHashSet}; use rustc_type_ir::{ - AliasTyKind, Flags, TypeFlags, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor, + AliasTyKind, TypeFoldable, inherent::{AdtDef, IntoKind, Region as _, SliceLike, Ty as _}, }; use stdx::never; @@ -61,7 +61,6 @@ use crate::{ coerce::{CoerceMany, DynamicCoerceMany}, diagnostics::{Diagnostics, InferenceTyLoweringContext as TyLoweringContext}, expr::ExprIsRead, - unify::InferenceTable, }, lower::{ ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, diagnostics::TyLoweringDiagnostic, @@ -69,10 +68,7 @@ use crate::{ mir::MirSpan, next_solver::{ AliasTy, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Region, Ty, TyKind, - Tys, - abi::Safety, - fold::fold_tys, - infer::traits::{Obligation, ObligationCause}, + Tys, abi::Safety, infer::traits::ObligationCause, }, traits::FnTrait, utils::TargetFeatureIsSafeInTarget, @@ -132,6 +128,8 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer ctx.infer_mut_body(); + ctx.handle_opaque_type_uses(); + ctx.type_inference_fallback(); // Comment from rustc: @@ -148,6 +146,10 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer ctx.infer_closures(); + ctx.table.select_obligations_where_possible(); + + ctx.handle_opaque_type_uses(); + Arc::new(ctx.resolve_all()) } @@ -454,7 +456,7 @@ pub struct InferenceResult<'db> { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub(crate) type_of_pat: ArenaMap<PatId, Ty<'db>>, pub(crate) type_of_binding: ArenaMap<BindingId, Ty<'db>>, - pub(crate) type_of_rpit: ArenaMap<ImplTraitIdx<'db>, Ty<'db>>, + pub(crate) type_of_opaque: FxHashMap<InternedOpaqueTyId, Ty<'db>>, type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch<'db>>, /// Whether there are any type-mismatching errors in the result. // FIXME: This isn't as useful as initially thought due to us falling back placeholders to @@ -499,7 +501,7 @@ impl<'db> InferenceResult<'db> { type_of_expr: Default::default(), type_of_pat: Default::default(), type_of_binding: Default::default(), - type_of_rpit: Default::default(), + type_of_opaque: Default::default(), type_mismatches: Default::default(), has_errors: Default::default(), error_ty, @@ -640,8 +642,14 @@ impl<'db> InferenceResult<'db> { // This method is consumed by external tools to run rust-analyzer as a library. Don't remove, please. pub fn return_position_impl_trait_types( &self, + db: &'db dyn HirDatabase, ) -> impl Iterator<Item = (ImplTraitIdx<'db>, Ty<'db>)> { - self.type_of_rpit.iter().map(|(k, v)| (k, *v)) + self.type_of_opaque.iter().filter_map(move |(&id, &ty)| { + let ImplTraitId::ReturnTypeImplTrait(_, rpit_idx) = id.loc(db) else { + return None; + }; + Some((rpit_idx, ty)) + }) } } @@ -707,6 +715,7 @@ struct InternedStandardTypes<'db> { re_static: Region<'db>, re_error: Region<'db>, + re_erased: Region<'db>, empty_args: GenericArgs<'db>, empty_tys: Tys<'db>, @@ -742,6 +751,7 @@ impl<'db> InternedStandardTypes<'db> { re_static, re_error: Region::error(interner), + re_erased: Region::new_erased(interner), empty_args: GenericArgs::new_from_iter(interner, []), empty_tys: Tys::new_from_iter(interner, []), @@ -848,11 +858,6 @@ fn find_continuable<'a, 'db>( } } -enum ImplTraitReplacingMode<'db> { - ReturnPosition(FxHashSet<Ty<'db>>), - TypeAlias, -} - impl<'body, 'db> InferenceContext<'body, 'db> { fn new( db: &'db dyn HirDatabase, @@ -861,7 +866,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { resolver: Resolver<'db>, ) -> Self { let trait_env = db.trait_environment_for_body(owner); - let table = unify::InferenceTable::new(db, trait_env); + let table = unify::InferenceTable::new(db, trait_env, Some(owner)); let types = InternedStandardTypes::new(table.interner()); InferenceContext { result: InferenceResult::new(types.error), @@ -952,7 +957,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // `InferenceResult` in the middle of inference. See the fixme comment in `consteval::eval_to_const`. If you // used this function for another workaround, mention it here. If you really need this function and believe that // there is no problem in it being `pub(crate)`, remove this comment. - pub(crate) fn resolve_all(self) -> InferenceResult<'db> { + fn resolve_all(self) -> InferenceResult<'db> { let InferenceContext { mut table, mut result, tuple_field_accesses_rev, diagnostics, .. } = self; @@ -967,7 +972,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { type_of_expr, type_of_pat, type_of_binding, - type_of_rpit, + type_of_opaque, type_mismatches, has_errors, error_ty: _, @@ -999,11 +1004,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { *has_errors = *has_errors || ty.references_non_lt_error(); } type_of_binding.shrink_to_fit(); - for ty in type_of_rpit.values_mut() { - *ty = table.resolve_completely(*ty); - *has_errors = *has_errors || ty.references_non_lt_error(); - } - type_of_rpit.shrink_to_fit(); + type_of_opaque.shrink_to_fit(); *has_errors |= !type_mismatches.is_empty(); @@ -1084,9 +1085,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container), ); - // Constants might be defining usage sites of TAITs. - self.make_tait_coercion_table(iter::once(return_ty)); - self.return_ty = return_ty; } @@ -1098,9 +1096,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { LifetimeElisionKind::Elided(self.types.re_static), ); - // Statics might be defining usage sites of TAITs. - self.make_tait_coercion_table(iter::once(return_ty)); - self.return_ty = return_ty; } @@ -1138,16 +1133,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { let ty = self.process_user_written_ty(ty); self.write_binding_ty(self_param, ty); } - let mut tait_candidates = FxHashSet::default(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.process_user_written_ty(ty); self.infer_top_pat(*pat, ty, None); - if ty.flags().intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER)) { - tait_candidates.insert(ty); - } } - let return_ty = match data.ret_type { + self.return_ty = match data.ret_type { Some(return_ty) => { let return_ty = self.with_ty_lowering( &data.store, @@ -1158,45 +1149,12 @@ impl<'body, 'db> InferenceContext<'body, 'db> { ctx.lower_ty(return_ty) }, ); - let return_ty = self.insert_type_vars(return_ty); - if let Some(rpits) = self.db.return_type_impl_traits(func) { - let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default()); - let result = self.insert_inference_vars_for_impl_trait(return_ty, &mut mode); - if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { - tait_candidates.extend(taits); - } - let rpits = (*rpits).as_ref().skip_binder(); - for (id, _) in rpits.impl_traits.iter() { - if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { - never!("Missed RPIT in `insert_inference_vars_for_rpit`"); - e.insert(self.types.error); - } - } - result - } else { - return_ty - } + self.process_user_written_ty(return_ty) } None => self.types.unit, }; - self.return_ty = self.process_user_written_ty(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty)); - - // Functions might be defining usage sites of TAITs. - // To define an TAITs, that TAIT must appear in the function's signatures. - // So, it suffices to check for params and return types. - fold_tys(self.interner(), self.return_ty, |ty| { - match ty.kind() { - TyKind::Alias(AliasTyKind::Opaque, _) | TyKind::Infer(..) => { - tait_candidates.insert(self.return_ty); - } - _ => {} - } - ty - }); - - self.make_tait_coercion_table(tait_candidates.iter().copied()); } #[inline] @@ -1204,193 +1162,6 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.table.interner() } - fn insert_inference_vars_for_impl_trait<T>( - &mut self, - t: T, - mode: &mut ImplTraitReplacingMode<'db>, - ) -> T - where - T: TypeFoldable<DbInterner<'db>>, - { - fold_tys(self.interner(), t, |ty| { - let ty = self.table.try_structurally_resolve_type(ty); - let opaque_ty_id = match ty.kind() { - TyKind::Alias(AliasTyKind::Opaque, alias_ty) => alias_ty.def_id.expect_opaque_ty(), - _ => return ty, - }; - let (impl_traits, idx) = match self.db.lookup_intern_impl_trait_id(opaque_ty_id) { - // We don't replace opaque types from other kind with inference vars - // because `insert_inference_vars_for_impl_traits` for each kinds - // and unreplaced opaque types of other kind are resolved while - // inferencing because of `tait_coercion_table`. - ImplTraitId::ReturnTypeImplTrait(def, idx) => { - if matches!(mode, ImplTraitReplacingMode::TypeAlias) { - // RPITs don't have `tait_coercion_table`, so use inserted inference - // vars for them. - if let Some(ty) = self.result.type_of_rpit.get(idx) { - return *ty; - } - return ty; - } - (self.db.return_type_impl_traits(def), idx) - } - ImplTraitId::TypeAliasImplTrait(def, idx) => { - if let ImplTraitReplacingMode::ReturnPosition(taits) = mode { - // Gather TAITs while replacing RPITs because TAITs inside RPITs - // may not visited while replacing TAITs - taits.insert(ty); - return ty; - } - (self.db.type_alias_impl_traits(def), idx) - } - }; - let Some(impl_traits) = impl_traits else { - return ty; - }; - let bounds = - (*impl_traits).as_ref().map_bound(|its| its.impl_traits[idx].predicates.as_slice()); - let var = match self.result.type_of_rpit.entry(idx) { - Entry::Occupied(entry) => return *entry.get(), - Entry::Vacant(entry) => *entry.insert(self.table.next_ty_var()), - }; - for clause in bounds.iter_identity_copied() { - let clause = self.insert_inference_vars_for_impl_trait(clause, mode); - self.table.register_predicate(Obligation::new( - self.interner(), - ObligationCause::new(), - self.table.trait_env.env, - clause, - )); - } - var - }) - } - - /// The coercion of a non-inference var into an opaque type should fail, - /// but not in the defining sites of the TAITs. - /// In such cases, we insert an proxy inference var for each TAIT, - /// and coerce into it instead of TAIT itself. - /// - /// The inference var stretagy is effective because; - /// - /// - It can still unify types that coerced into TAITs - /// - We are pushing `impl Trait` bounds into it - /// - /// This function inserts a map that maps the opaque type to that proxy inference var. - fn make_tait_coercion_table(&mut self, tait_candidates: impl Iterator<Item = Ty<'db>>) { - struct TypeAliasImplTraitCollector<'a, 'db> { - db: &'a dyn HirDatabase, - table: &'a mut InferenceTable<'db>, - assocs: FxHashMap<InternedOpaqueTyId, (ImplId, Ty<'db>)>, - non_assocs: FxHashMap<InternedOpaqueTyId, Ty<'db>>, - } - - impl<'db> TypeVisitor<DbInterner<'db>> for TypeAliasImplTraitCollector<'_, 'db> { - type Result = (); - - fn visit_ty(&mut self, ty: Ty<'db>) { - let ty = self.table.try_structurally_resolve_type(ty); - - if let TyKind::Alias(AliasTyKind::Opaque, alias_ty) = ty.kind() - && let id = alias_ty.def_id.expect_opaque_ty() - && let ImplTraitId::TypeAliasImplTrait(alias_id, _) = - self.db.lookup_intern_impl_trait_id(id) - { - let loc = self.db.lookup_intern_type_alias(alias_id); - match loc.container { - ItemContainerId::ImplId(impl_id) => { - self.assocs.insert(id, (impl_id, ty)); - } - ItemContainerId::ModuleId(..) | ItemContainerId::ExternBlockId(..) => { - self.non_assocs.insert(id, ty); - } - _ => {} - } - } - - ty.super_visit_with(self) - } - } - - let mut collector = TypeAliasImplTraitCollector { - db: self.db, - table: &mut self.table, - assocs: FxHashMap::default(), - non_assocs: FxHashMap::default(), - }; - for ty in tait_candidates { - ty.visit_with(&mut collector); - } - - // Non-assoc TAITs can be define-used everywhere as long as they are - // in function signatures or const types, etc - let mut taits = collector.non_assocs; - - // assoc TAITs(ATPITs) can be only define-used inside their impl block. - // They cannot be define-used in inner items like in the following; - // - // ``` - // impl Trait for Struct { - // type Assoc = impl Default; - // - // fn assoc_fn() -> Self::Assoc { - // let foo: Self::Assoc = true; // Allowed here - // - // fn inner() -> Self::Assoc { - // false // Not allowed here - // } - // - // foo - // } - // } - // ``` - let impl_id = match self.owner { - DefWithBodyId::FunctionId(it) => { - let loc = self.db.lookup_intern_function(it); - if let ItemContainerId::ImplId(impl_id) = loc.container { - Some(impl_id) - } else { - None - } - } - DefWithBodyId::ConstId(it) => { - let loc = self.db.lookup_intern_const(it); - if let ItemContainerId::ImplId(impl_id) = loc.container { - Some(impl_id) - } else { - None - } - } - _ => None, - }; - - if let Some(impl_id) = impl_id { - taits.extend(collector.assocs.into_iter().filter_map(|(id, (impl_, ty))| { - if impl_ == impl_id { Some((id, ty)) } else { None } - })); - } - - let tait_coercion_table: FxHashMap<_, _> = taits - .into_iter() - .filter_map(|(id, ty)| { - if let ImplTraitId::TypeAliasImplTrait(..) = self.db.lookup_intern_impl_trait_id(id) - { - let ty = self.insert_inference_vars_for_impl_trait( - ty, - &mut ImplTraitReplacingMode::TypeAlias, - ); - Some((id, ty)) - } else { - None - } - }) - .collect(); - - if !tait_coercion_table.is_empty() { - self.table.tait_coercion_table = Some(tait_coercion_table); - } - } - fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), @@ -2006,12 +1777,15 @@ impl<'body, 'db> InferenceContext<'body, 'db> { Some(struct_.into()) } - fn get_traits_in_scope(&self) -> Either<FxHashSet<TraitId>, &FxHashSet<TraitId>> { - let mut b_traits = self.resolver.traits_in_scope_from_block_scopes().peekable(); + fn get_traits_in_scope<'a>( + resolver: &Resolver<'db>, + traits_in_scope: &'a FxHashSet<TraitId>, + ) -> Either<FxHashSet<TraitId>, &'a FxHashSet<TraitId>> { + let mut b_traits = resolver.traits_in_scope_from_block_scopes().peekable(); if b_traits.peek().is_some() { - Either::Left(self.traits_in_scope.iter().copied().chain(b_traits).collect()) + Either::Left(traits_in_scope.iter().copied().chain(b_traits).collect()) } else { - Either::Right(&self.traits_in_scope) + Either::Right(traits_in_scope) } } } |