From d2aba91a0c58111b9a5df1e2a1f26b8caf45be2e Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sat, 16 Mar 2024 03:31:12 +0900 Subject: [PATCH] feat: Implement ATPIT --- crates/hir-ty/src/chalk_db.rs | 13 ++ crates/hir-ty/src/chalk_ext.rs | 14 ++ crates/hir-ty/src/db.rs | 16 +-- crates/hir-ty/src/display.rs | 28 ++++ crates/hir-ty/src/infer.rs | 151 +++++++++++++++++++--- crates/hir-ty/src/infer/coerce.rs | 17 +++ crates/hir-ty/src/infer/path.rs | 6 +- crates/hir-ty/src/infer/unify.rs | 8 +- crates/hir-ty/src/layout.rs | 3 + crates/hir-ty/src/lib.rs | 13 +- crates/hir-ty/src/lower.rs | 73 ++++++++--- crates/hir-ty/src/mir/monomorphization.rs | 3 + crates/hir-ty/src/tests/traits.rs | 75 +++++++++++ 13 files changed, 365 insertions(+), 55 deletions(-) diff --git a/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index e678a2fee1..46612242b0 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/crates/hir-ty/src/chalk_db.rs @@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { }; chalk_ir::Binders::new(binders, bound) } + crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = self + .db + .type_alias_impl_traits(alias) + .expect("impl trait id without impl traits"); + let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders(); + let data = &datas.impl_traits[idx]; + let bound = OpaqueTyDatumBound { + bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()), + where_clauses: chalk_ir::Binders::empty(Interner, vec![]), + }; + chalk_ir::Binders::new(binders, bound) + } crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => { if let Some((future_trait, future_output)) = self .db diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index 795a599691..d1aebeff26 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -268,6 +268,13 @@ impl TyExt for Ty { data.substitute(Interner, &subst).into_value_and_skipped_binders().0 }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &subst).into_value_and_skipped_binders().0 + }) + } } } TyKind::Alias(AliasTy::Opaque(opaque_ty)) => { @@ -280,6 +287,13 @@ impl TyExt for Ty { data.substitute(Interner, &opaque_ty.substitution) }) } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + db.type_alias_impl_traits(alias).map(|it| { + let data = + (*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + data.substitute(Interner, &opaque_ty.substitution) + }) + } // It always has an parameter for Future::Output type. ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(), }; diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 28c497989f..90bf46b505 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -23,9 +23,9 @@ use crate::{ layout::{Layout, LayoutError}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, - Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult, - Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution, - TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, + Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits, + InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment, + TraitRef, Ty, TyDefId, ValueTyDefId, }; use hir_expand::name::Name; @@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast { fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; #[salsa::invoke(crate::lower::return_type_impl_traits)] - fn return_type_impl_traits( - &self, - def: FunctionId, - ) -> Option>>; + fn return_type_impl_traits(&self, def: FunctionId) -> Option>>; + + #[salsa::invoke(crate::lower::type_alias_impl_traits)] + fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option>>; #[salsa::invoke(crate::lower::generic_predicates_for_param_query)] #[salsa::cycle(crate::lower::generic_predicates_for_param_recover)] diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 20964f5acb..269db57bc3 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1063,6 +1063,20 @@ impl HirDisplay for Ty { )?; // FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, ¶meters); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => { let future_trait = db .lang_item(body.module(db.upcast()).krate(), LangItem::Future) @@ -1228,6 +1242,20 @@ impl HirDisplay for Ty { SizedByDefault::Sized { anchor: krate }, )?; } + ImplTraitId::AssociatedTypeImplTrait(alias, idx) => { + let datas = + db.type_alias_impl_traits(alias).expect("impl trait id without data"); + let data = + (*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone()); + let bounds = data.substitute(Interner, &opaque_ty.substitution); + let krate = alias.krate(db.upcast()); + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + bounds.skip_binders(), + SizedByDefault::Sized { anchor: krate }, + )?; + } ImplTraitId::AsyncBlockTypeImplTrait(..) => { write!(f, "{{async block}}")?; } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 34ba17f145..8847f7e24f 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -25,8 +25,11 @@ pub(crate) mod unify; use std::{convert::identity, iter, ops::Index}; use chalk_ir::{ - cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety, - Scalar, TyKind, TypeFlags, Variance, + cast::Cast, + fold::TypeFoldable, + interner::HasInterner, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance, }; use either::Either; use hir_def::{ @@ -53,14 +56,14 @@ use triomphe::Arc; use crate::{ db::HirDatabase, fold_tys, - infer::coerce::CoerceMany, + infer::{coerce::CoerceMany, unify::InferenceTable}, lower::ImplTraitLoweringMode, static_lifetime, to_assoc_type_id, traits::FnTrait, utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder}, AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId, - InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment, - TraitRef, Ty, TyBuilder, TyExt, + ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, }; // This lint has a false positive here. See the link below for details. @@ -422,7 +425,7 @@ pub struct InferenceResult { /// unresolved or missing subpatterns or subpatterns of mismatched types. pub type_of_pat: ArenaMap, pub type_of_binding: ArenaMap, - pub type_of_rpit: ArenaMap, + pub type_of_rpit: ArenaMap, /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop. pub type_of_for_iterator: FxHashMap, type_mismatches: FxHashMap, @@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> { } fn collect_const(&mut self, data: &ConstData) { - self.return_ty = self.make_ty(&data.type_ref); + let return_ty = self.make_ty(&data.type_ref); + + // Constants might be associated items that define ATPITs. + self.insert_atpit_coercion_table(iter::once(&return_ty)); + + self.return_ty = return_ty; } fn collect_static(&mut self, data: &StaticData) { @@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> { self.write_binding_ty(self_param, ty); } } + let mut params_and_ret_tys = Vec::new(); for (ty, pat) in param_tys.zip(&*self.body.params) { let ty = self.insert_type_vars(ty); let ty = self.normalize_associated_types_in(ty); self.infer_top_pat(*pat, &ty); + params_and_ret_tys.push(ty); } let return_ty = &*data.ret_type; @@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> { let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) { // RPIT opaque types use substitution of their parent function. let fn_placeholders = TyBuilder::placeholder_subst(self.db, func); - let result = - self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders); + let result = self.insert_inference_vars_for_impl_trait( + return_ty, + rpits.clone(), + fn_placeholders, + ); let rpits = rpits.skip_binders(); for (id, _) in rpits.impl_traits.iter() { if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) { @@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> { self.return_ty = self.normalize_associated_types_in(return_ty); self.return_coercion = Some(CoerceMany::new(self.return_ty.clone())); + + // Functions might be associated items that define ATPITs. + // To define an ATPITs, that ATPIT must appear in the function's signitures. + // So, it suffices to check for params and return types. + params_and_ret_tys.push(self.return_ty.clone()); + self.insert_atpit_coercion_table(params_and_ret_tys.iter()); } - fn insert_inference_vars_for_rpit( + fn insert_inference_vars_for_impl_trait( &mut self, t: T, - rpits: Arc>, - fn_placeholders: Substitution, + rpits: Arc>, + placeholders: Substitution, ) -> T where T: crate::HasInterner + crate::TypeFoldable, @@ -837,6 +856,7 @@ impl<'a> InferenceContext<'a> { }; let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) { ImplTraitId::ReturnTypeImplTrait(_, idx) => idx, + ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx, _ => unreachable!(), }; let bounds = @@ -844,15 +864,14 @@ impl<'a> InferenceContext<'a> { let var = self.table.new_type_var(); let var_subst = Substitution::from1(Interner, var.clone()); for bound in bounds { - let predicate = - bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders); + let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders); let (var_predicate, binders) = predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders(); always!(binders.is_empty(Interner)); // quantified where clauses not yet handled - let var_predicate = self.insert_inference_vars_for_rpit( + let var_predicate = self.insert_inference_vars_for_impl_trait( var_predicate, rpits.clone(), - fn_placeholders.clone(), + placeholders.clone(), ); self.push_obligation(var_predicate.cast(Interner)); } @@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> { ) } + /// The coercion of a non-inference var into an opaque type should fail, + /// but not in the defining sites of the ATPITs. + /// In such cases, we insert an proxy inference var for each ATPIT, + /// and coerce into it instead of ATPIT itself. + /// + /// The inference var stretagy is effective because; + /// + /// - It can still unify types that coerced into ATPIT + /// - We are pushing `impl Trait` bounds into it + /// + /// This function inserts a map that maps the opaque type to that proxy inference var. + fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator) { + struct OpaqueTyCollector<'a, 'b> { + table: &'b mut InferenceTable<'a>, + opaque_tys: FxHashMap, + } + + impl<'a, 'b> TypeVisitor for OpaqueTyCollector<'a, 'b> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty( + &mut self, + ty: &chalk_ir::Ty, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + let ty = self.table.resolve_ty_shallow(ty); + + if let TyKind::OpaqueType(id, _) = ty.kind(Interner) { + self.opaque_tys.insert(*id, ty.clone()); + } + + ty.super_visit_with(self, outer_binder) + } + } + + // Early return if this is not happening inside the impl block + let impl_id = if let Some(impl_id) = self.resolver.impl_def() { + impl_id + } else { + return; + }; + + let assoc_tys: FxHashSet<_> = self + .db + .impl_data(impl_id) + .items + .iter() + .filter_map(|item| match item { + AssocItemId::TypeAliasId(alias) => Some(*alias), + _ => None, + }) + .collect(); + if assoc_tys.is_empty() { + return; + } + + let mut collector = + OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() }; + for ty in tys { + ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST); + } + let atpit_coercion_table: FxHashMap<_, _> = collector + .opaque_tys + .into_iter() + .filter_map(|(opaque_ty_id, ty)| { + if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) = + self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) + { + if assoc_tys.contains(&alias_id) { + let atpits = self + .db + .type_alias_impl_traits(alias_id) + .expect("Marked as ATPIT but no impl traits!"); + let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id); + let ty = self.insert_inference_vars_for_impl_trait( + ty, + atpits, + alias_placeholders, + ); + return Some((opaque_ty_id, ty)); + } + } + + None + }) + .collect(); + + if !atpit_coercion_table.is_empty() { + self.table.atpit_coercion_table = Some(atpit_coercion_table); + } + } + fn infer_body(&mut self) { match self.return_coercion { Some(_) => self.infer_return(self.body.body_expr), diff --git a/crates/hir-ty/src/infer/coerce.rs b/crates/hir-ty/src/infer/coerce.rs index ff6de61ba6..fba37dbcff 100644 --- a/crates/hir-ty/src/infer/coerce.rs +++ b/crates/hir-ty/src/infer/coerce.rs @@ -276,6 +276,23 @@ impl InferenceTable<'_> { return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]); } + // If we are coercing into an ATPIT, coerce into its proxy inference var, instead. + let mut to_ty = to_ty; + let mut _to = None; + if let Some(atpit_table) = &self.atpit_coercion_table { + if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) { + if !matches!( + from_ty.kind(Interner), + TyKind::InferenceVar(..) | TyKind::OpaqueType(..) + ) { + if let Some(ty) = atpit_table.get(opaque_ty_id) { + _to = Some(ty.clone()); + to_ty = _to.as_ref().unwrap(); + } + } + } + } + // Consider coercing the subtype to a DST if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) { return Ok(ret); diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 8f537bb448..44b35b2ebb 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -15,11 +15,11 @@ use crate::{ method_resolution::{self, VisibleFromModule}, to_chalk_trait_id, utils::generics, - InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, - ValueTyDefId, + InferenceDiagnostic, Interner, Substitution, TraitRef, TraitRefExt, Ty, TyBuilder, TyExt, + TyKind, ValueTyDefId, }; -use super::{ExprOrPatId, InferenceContext, TraitRef}; +use super::{ExprOrPatId, InferenceContext}; impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index be7547f9ba..93fd54455b 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -10,6 +10,7 @@ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; use hir_expand::name; +use rustc_hash::FxHashMap; use smallvec::SmallVec; use triomphe::Arc; @@ -18,8 +19,9 @@ use crate::{ consteval::unknown_const, db::HirDatabase, fold_tys_and_consts, static_lifetime, to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, - InferenceVar, Interner, Lifetime, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, Solution, - Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, + InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, + Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + WhereClause, }; impl InferenceContext<'_> { @@ -239,6 +241,7 @@ type ChalkInferenceTable = chalk_solve::infer::InferenceTable; pub(crate) struct InferenceTable<'a> { pub(crate) db: &'a dyn HirDatabase, pub(crate) trait_env: Arc, + pub(crate) atpit_coercion_table: Option>, var_unification_table: ChalkInferenceTable, type_variable_table: SmallVec<[TypeVariableFlags; 16]>, pending_obligations: Vec>>, @@ -258,6 +261,7 @@ impl<'a> InferenceTable<'a> { InferenceTable { db, trait_env, + atpit_coercion_table: None, var_unification_table: ChalkInferenceTable::new(), type_variable_table: SmallVec::new(), pending_obligations: Vec::new(), diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 9655981cc9..dd949e26c2 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs @@ -389,6 +389,9 @@ pub fn layout_of_ty_query( let infer = db.infer(func.into()); return db.layout_of_ty(infer.type_of_rpit[idx].clone(), trait_env); } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + return Err(LayoutError::NotImplemented); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { return Err(LayoutError::NotImplemented) } diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index ec97bdc2c4..a7affc5f92 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -584,24 +584,25 @@ impl TypeFoldable for CallableSig { #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub enum ImplTraitId { - ReturnTypeImplTrait(hir_def::FunctionId, RpitId), + ReturnTypeImplTrait(hir_def::FunctionId, ImplTraitIdx), + AssociatedTypeImplTrait(hir_def::TypeAliasId, ImplTraitIdx), AsyncBlockTypeImplTrait(hir_def::DefWithBodyId, ExprId), } impl_intern_value_trivial!(ImplTraitId); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTraits { - pub(crate) impl_traits: Arena, +pub struct ImplTraits { + pub(crate) impl_traits: Arena, } -has_interner!(ReturnTypeImplTraits); +has_interner!(ImplTraits); #[derive(Clone, PartialEq, Eq, Debug, Hash)] -pub struct ReturnTypeImplTrait { +pub struct ImplTrait { pub(crate) bounds: Binders>, } -pub type RpitId = Idx; +pub type ImplTraitIdx = Idx; pub fn static_lifetime() -> Lifetime { LifetimeData::Static.intern(Interner) diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index dac20f2259..d65fd4a71c 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -61,9 +61,9 @@ use crate::{ InTypeConstIdMetadata, }, AliasEq, AliasTy, Binders, BoundVar, CallableSig, Const, ConstScalar, DebruijnIndex, DynTy, - FnAbi, FnPointer, FnSig, FnSubst, ImplTraitId, Interner, ParamKind, PolyFnSig, ProjectionTy, - QuantifiedWhereClause, QuantifiedWhereClauses, ReturnTypeImplTrait, ReturnTypeImplTraits, - Substitution, TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, + FnAbi, FnPointer, FnSig, FnSubst, ImplTrait, ImplTraitId, ImplTraits, Interner, ParamKind, + PolyFnSig, ProjectionTy, QuantifiedWhereClause, QuantifiedWhereClauses, Substitution, + TraitEnvironment, TraitRef, TraitRefExt, Ty, TyBuilder, TyKind, WhereClause, }; #[derive(Debug)] @@ -76,7 +76,7 @@ enum ImplTraitLoweringState { /// we're grouping the mutable data (the counter and this field) together /// with the immutable context (the references to the DB and resolver). /// Splitting this up would be a possible fix. - Opaque(RefCell>), + Opaque(RefCell>), Param(Cell), Variable(Cell), Disallowed, @@ -301,15 +301,22 @@ impl<'a> TyLoweringContext<'a> { TypeRef::ImplTrait(bounds) => { match &self.impl_trait_mode { ImplTraitLoweringState::Opaque(opaque_type_data) => { - let func = match self.resolver.generic_def() { - Some(GenericDefId::FunctionId(f)) => f, - _ => panic!("opaque impl trait lowering in non-function"), + let (origin, krate) = match self.resolver.generic_def() { + Some(GenericDefId::FunctionId(f)) => { + (Either::Left(f), f.krate(self.db.upcast())) + } + Some(GenericDefId::TypeAliasId(a)) => { + (Either::Right(a), a.krate(self.db.upcast())) + } + _ => panic!( + "opaque impl trait lowering must be in function or type alias" + ), }; // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = opaque_type_data.borrow_mut().alloc(ReturnTypeImplTrait { + let idx = opaque_type_data.borrow_mut().alloc(ImplTrait { bounds: crate::make_single_type_binders(Vec::new()), }); // We don't want to lower the bounds inside the binders @@ -323,13 +330,17 @@ impl<'a> TyLoweringContext<'a> { // away instead of two. let actual_opaque_type_data = self .with_debruijn(DebruijnIndex::INNERMOST, |ctx| { - ctx.lower_impl_trait(bounds, func) + ctx.lower_impl_trait(bounds, krate) }); opaque_type_data.borrow_mut()[idx] = actual_opaque_type_data; - let impl_trait_id = ImplTraitId::ReturnTypeImplTrait(func, idx); + let impl_trait_id = origin.either( + |f| ImplTraitId::ReturnTypeImplTrait(f, idx), + |a| ImplTraitId::AssociatedTypeImplTrait(a, idx), + ); let opaque_ty_id = self.db.intern_impl_trait_id(impl_trait_id).into(); - let generics = generics(self.db.upcast(), func.into()); + let generics = + generics(self.db.upcast(), origin.either(|f| f.into(), |a| a.into())); let parameters = generics.bound_vars_subst(self.db, self.in_binders); TyKind::OpaqueType(opaque_ty_id, parameters).intern(Interner) } @@ -1270,11 +1281,7 @@ impl<'a> TyLoweringContext<'a> { } } - fn lower_impl_trait( - &self, - bounds: &[Interned], - func: FunctionId, - ) -> ReturnTypeImplTrait { + fn lower_impl_trait(&self, bounds: &[Interned], krate: CrateId) -> ImplTrait { cov_mark::hit!(lower_rpit); let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); let predicates = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { @@ -1284,7 +1291,6 @@ impl<'a> TyLoweringContext<'a> { .collect(); if !ctx.unsized_types.borrow().contains(&self_ty) { - let krate = func.krate(ctx.db.upcast()); let sized_trait = ctx .db .lang_item(krate, LangItem::Sized) @@ -1301,7 +1307,7 @@ impl<'a> TyLoweringContext<'a> { } predicates }); - ReturnTypeImplTrait { bounds: crate::make_single_type_binders(predicates) } + ImplTrait { bounds: crate::make_single_type_binders(predicates) } } } @@ -1869,6 +1875,7 @@ fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders { let generics = generics(db.upcast(), t.into()); let resolver = t.resolver(db.upcast()); let ctx = TyLoweringContext::new(db, &resolver, t.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) .with_type_param_mode(ParamLoweringMode::Variable); let type_alias_data = db.type_alias_data(t); if type_alias_data.is_extern { @@ -2029,7 +2036,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, def: hir_def::FunctionId, -) -> Option>> { +) -> Option>> { // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe let data = db.function_data(def); let resolver = def.resolver(db.upcast()); @@ -2038,7 +2045,7 @@ pub(crate) fn return_type_impl_traits( .with_type_param_mode(ParamLoweringMode::Variable); let _ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let return_type_impl_traits = ReturnTypeImplTraits { + let return_type_impl_traits = ImplTraits { impl_traits: match ctx_ret.impl_trait_mode { ImplTraitLoweringState::Opaque(x) => x.into_inner(), _ => unreachable!(), @@ -2051,6 +2058,32 @@ pub(crate) fn return_type_impl_traits( } } +pub(crate) fn type_alias_impl_traits( + db: &dyn HirDatabase, + def: hir_def::TypeAliasId, +) -> Option>> { + let data = db.type_alias_data(def); + let resolver = def.resolver(db.upcast()); + let ctx = TyLoweringContext::new(db, &resolver, def.into()) + .with_impl_trait_mode(ImplTraitLoweringMode::Opaque) + .with_type_param_mode(ParamLoweringMode::Variable); + if let Some(type_ref) = &data.type_ref { + let _ty = ctx.lower_ty(type_ref); + } + let generics = generics(db.upcast(), def.into()); + let type_alias_impl_traits = ImplTraits { + impl_traits: match ctx.impl_trait_mode { + ImplTraitLoweringState::Opaque(x) => x.into_inner(), + _ => unreachable!(), + }, + }; + if type_alias_impl_traits.impl_traits.is_empty() { + None + } else { + Some(Arc::new(make_binders(db, &generics, type_alias_impl_traits))) + } +} + pub(crate) fn lower_to_chalk_mutability(m: hir_def::type_ref::Mutability) -> Mutability { match m { hir_def::type_ref::Mutability::Shared => Mutability::Not, diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs index d2e413f0a3..12ad67cdc4 100644 --- a/crates/hir-ty/src/mir/monomorphization.rs +++ b/crates/hir-ty/src/mir/monomorphization.rs @@ -82,6 +82,9 @@ impl FallibleTypeFolder for Filler<'_> { }; filler.try_fold_ty(infer.type_of_rpit[idx].clone(), outer_binder) } + crate::ImplTraitId::AssociatedTypeImplTrait(..) => { + not_supported!("associated type impl trait"); + } crate::ImplTraitId::AsyncBlockTypeImplTrait(_, _) => { not_supported!("async block impl trait"); } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index b80cfe18e4..83bb90b617 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4655,3 +4655,78 @@ fn f() { "#, ); } + +#[test] +fn associated_type_impl_trait() { + check_types( + r#" +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Self::Item; +} +struct S2; +impl Bar for S2 { + type Item = impl Foo; + fn bar(&self) -> Self::Item { + S1 + } +} + +fn test() { + let x = S2.bar(); + //^ impl Foo + ?Sized +} + "#, + ); +} + +#[test] +fn associated_type_impl_traits_complex() { + check_types( + r#" +struct Unary(T); +struct Binary(T, U); + +trait Foo {} +struct S1; +impl Foo for S1 {} + +trait Bar { + type Item; + fn bar(&self) -> Unary; +} +struct S2; +impl Bar for S2 { + type Item = Unary; + fn bar(&self) -> Unary<::Item> { + Unary(Unary(S1)) + } +} + +trait Baz { + type Target1; + type Target2; + fn baz(&self) -> Binary; +} +struct S3; +impl Baz for S3 { + type Target1 = impl Foo; + type Target2 = Unary; + fn baz(&self) -> Binary { + Binary(S1, Unary(S2)) + } +} + +fn test() { + let x = S3.baz(); + //^ Binary> + let y = x.1.0.bar(); + //^ Unary> +} + "#, + ); +}