Skip to content

Commit d3eeadc

Browse files
committed
Auto merge of #16852 - ShoyuVanilla:atpit, r=Veykril
feat: Implement ATPIT Resolves #16584 Note: This implementation only works for ATPIT, not for TAIT. The main hinderence that blocks the later is the defining sites of TAIT can be inner blocks like in; ```rust type X = impl Default; mod foo { fn bar() -> super::X { () } } ``` So, to figure out we are defining it or not, we should recursively probe for nested modules and bodies. For ATPIT, we can just look into current body because `error[E0401]: can't use 'Self' from outer item` prevent such nested structures; ```rust trait Foo { type Item; fn foo() -> Self::Item; } struct Bar; impl Foo for Bar { type Item = impl Default; fn foo() -> Self::Item { fn bar() -> Self::Item { ^^^^^^^^^^ | use of `Self` from outer item refer to the type directly here instead 5 } bar() } } ``` But this implementation does not checks for unification of same ATPIT between different bodies, monomorphization, nor layout for similar reason. (But these can be done with lazyness if we can utilize something like "mutation of interned value" with `db`. I coundn't find such thing but I would appreciate it if such thing exists and you could let me know 😅)
2 parents 7c2bb75 + d034ab0 commit d3eeadc

File tree

13 files changed

+361
-55
lines changed

13 files changed

+361
-55
lines changed

crates/hir-ty/src/chalk_db.rs

+13
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,19 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
272272
};
273273
chalk_ir::Binders::new(binders, bound)
274274
}
275+
crate::ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
276+
let datas = self
277+
.db
278+
.type_alias_impl_traits(alias)
279+
.expect("impl trait id without impl traits");
280+
let (datas, binders) = (*datas).as_ref().into_value_and_skipped_binders();
281+
let data = &datas.impl_traits[idx];
282+
let bound = OpaqueTyDatumBound {
283+
bounds: make_single_type_binders(data.bounds.skip_binders().to_vec()),
284+
where_clauses: chalk_ir::Binders::empty(Interner, vec![]),
285+
};
286+
chalk_ir::Binders::new(binders, bound)
287+
}
275288
crate::ImplTraitId::AsyncBlockTypeImplTrait(..) => {
276289
if let Some((future_trait, future_output)) = self
277290
.db

crates/hir-ty/src/chalk_ext.rs

+14
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,13 @@ impl TyExt for Ty {
268268
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
269269
})
270270
}
271+
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
272+
db.type_alias_impl_traits(alias).map(|it| {
273+
let data =
274+
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
275+
data.substitute(Interner, &subst).into_value_and_skipped_binders().0
276+
})
277+
}
271278
}
272279
}
273280
TyKind::Alias(AliasTy::Opaque(opaque_ty)) => {
@@ -280,6 +287,13 @@ impl TyExt for Ty {
280287
data.substitute(Interner, &opaque_ty.substitution)
281288
})
282289
}
290+
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
291+
db.type_alias_impl_traits(alias).map(|it| {
292+
let data =
293+
(*it).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
294+
data.substitute(Interner, &opaque_ty.substitution)
295+
})
296+
}
283297
// It always has an parameter for Future::Output type.
284298
ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
285299
};

crates/hir-ty/src/db.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use base_db::{
1111
use hir_def::{
1212
db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, ConstParamId,
1313
DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId,
14-
LifetimeParamId, LocalFieldId, StaticId, TypeOrConstParamId, VariantId,
14+
LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId,
1515
};
1616
use la_arena::ArenaMap;
1717
use smallvec::SmallVec;
@@ -23,9 +23,9 @@ use crate::{
2323
layout::{Layout, LayoutError},
2424
method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
2525
mir::{BorrowckResult, MirBody, MirLowerError},
26-
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, InferenceResult,
27-
Interner, PolyFnSig, QuantifiedWhereClause, ReturnTypeImplTraits, Substitution,
28-
TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
26+
Binders, CallableDefId, ClosureId, Const, FnDefId, GenericArg, ImplTraitId, ImplTraits,
27+
InferenceResult, Interner, PolyFnSig, QuantifiedWhereClause, Substitution, TraitEnvironment,
28+
TraitRef, Ty, TyDefId, ValueTyDefId,
2929
};
3030
use hir_expand::name::Name;
3131

@@ -132,10 +132,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
132132
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;
133133

134134
#[salsa::invoke(crate::lower::return_type_impl_traits)]
135-
fn return_type_impl_traits(
136-
&self,
137-
def: FunctionId,
138-
) -> Option<Arc<Binders<ReturnTypeImplTraits>>>;
135+
fn return_type_impl_traits(&self, def: FunctionId) -> Option<Arc<Binders<ImplTraits>>>;
136+
137+
#[salsa::invoke(crate::lower::type_alias_impl_traits)]
138+
fn type_alias_impl_traits(&self, def: TypeAliasId) -> Option<Arc<Binders<ImplTraits>>>;
139139

140140
#[salsa::invoke(crate::lower::generic_predicates_for_param_query)]
141141
#[salsa::cycle(crate::lower::generic_predicates_for_param_recover)]

crates/hir-ty/src/display.rs

+28
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,20 @@ impl HirDisplay for Ty {
10631063
)?;
10641064
// FIXME: it would maybe be good to distinguish this from the alias type (when debug printing), and to show the substitution
10651065
}
1066+
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
1067+
let datas =
1068+
db.type_alias_impl_traits(alias).expect("impl trait id without data");
1069+
let data =
1070+
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
1071+
let bounds = data.substitute(Interner, &parameters);
1072+
let krate = alias.krate(db.upcast());
1073+
write_bounds_like_dyn_trait_with_prefix(
1074+
f,
1075+
"impl",
1076+
bounds.skip_binders(),
1077+
SizedByDefault::Sized { anchor: krate },
1078+
)?;
1079+
}
10661080
ImplTraitId::AsyncBlockTypeImplTrait(body, ..) => {
10671081
let future_trait = db
10681082
.lang_item(body.module(db.upcast()).krate(), LangItem::Future)
@@ -1228,6 +1242,20 @@ impl HirDisplay for Ty {
12281242
SizedByDefault::Sized { anchor: krate },
12291243
)?;
12301244
}
1245+
ImplTraitId::AssociatedTypeImplTrait(alias, idx) => {
1246+
let datas =
1247+
db.type_alias_impl_traits(alias).expect("impl trait id without data");
1248+
let data =
1249+
(*datas).as_ref().map(|rpit| rpit.impl_traits[idx].bounds.clone());
1250+
let bounds = data.substitute(Interner, &opaque_ty.substitution);
1251+
let krate = alias.krate(db.upcast());
1252+
write_bounds_like_dyn_trait_with_prefix(
1253+
f,
1254+
"impl",
1255+
bounds.skip_binders(),
1256+
SizedByDefault::Sized { anchor: krate },
1257+
)?;
1258+
}
12311259
ImplTraitId::AsyncBlockTypeImplTrait(..) => {
12321260
write!(f, "{{async block}}")?;
12331261
}

crates/hir-ty/src/infer.rs

+135-16
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ pub(crate) mod unify;
2525
use std::{convert::identity, iter, ops::Index};
2626

2727
use chalk_ir::{
28-
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
29-
Scalar, TyKind, TypeFlags, Variance,
28+
cast::Cast,
29+
fold::TypeFoldable,
30+
interner::HasInterner,
31+
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
32+
DebruijnIndex, Mutability, Safety, Scalar, TyKind, TypeFlags, Variance,
3033
};
3134
use either::Either;
3235
use hir_def::{
@@ -53,14 +56,14 @@ use triomphe::Arc;
5356
use crate::{
5457
db::HirDatabase,
5558
fold_tys,
56-
infer::coerce::CoerceMany,
59+
infer::{coerce::CoerceMany, unify::InferenceTable},
5760
lower::ImplTraitLoweringMode,
5861
static_lifetime, to_assoc_type_id,
5962
traits::FnTrait,
6063
utils::{InTypeConstIdMetadata, UnevaluatedConstEvaluatorFolder},
6164
AliasEq, AliasTy, Binders, ClosureId, Const, DomainGoal, GenericArg, Goal, ImplTraitId,
62-
InEnvironment, Interner, Lifetime, ProjectionTy, RpitId, Substitution, TraitEnvironment,
63-
TraitRef, Ty, TyBuilder, TyExt,
65+
ImplTraitIdx, InEnvironment, Interner, Lifetime, OpaqueTyId, ProjectionTy, Substitution,
66+
TraitEnvironment, Ty, TyBuilder, TyExt,
6467
};
6568

6669
// This lint has a false positive here. See the link below for details.
@@ -422,7 +425,7 @@ pub struct InferenceResult {
422425
/// unresolved or missing subpatterns or subpatterns of mismatched types.
423426
pub type_of_pat: ArenaMap<PatId, Ty>,
424427
pub type_of_binding: ArenaMap<BindingId, Ty>,
425-
pub type_of_rpit: ArenaMap<RpitId, Ty>,
428+
pub type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
426429
/// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
427430
pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
428431
type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
@@ -752,7 +755,12 @@ impl<'a> InferenceContext<'a> {
752755
}
753756

754757
fn collect_const(&mut self, data: &ConstData) {
755-
self.return_ty = self.make_ty(&data.type_ref);
758+
let return_ty = self.make_ty(&data.type_ref);
759+
760+
// Constants might be associated items that define ATPITs.
761+
self.insert_atpit_coercion_table(iter::once(&return_ty));
762+
763+
self.return_ty = return_ty;
756764
}
757765

758766
fn collect_static(&mut self, data: &StaticData) {
@@ -785,11 +793,13 @@ impl<'a> InferenceContext<'a> {
785793
self.write_binding_ty(self_param, ty);
786794
}
787795
}
796+
let mut params_and_ret_tys = Vec::new();
788797
for (ty, pat) in param_tys.zip(&*self.body.params) {
789798
let ty = self.insert_type_vars(ty);
790799
let ty = self.normalize_associated_types_in(ty);
791800

792801
self.infer_top_pat(*pat, &ty);
802+
params_and_ret_tys.push(ty);
793803
}
794804
let return_ty = &*data.ret_type;
795805

@@ -801,8 +811,11 @@ impl<'a> InferenceContext<'a> {
801811
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
802812
// RPIT opaque types use substitution of their parent function.
803813
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
804-
let result =
805-
self.insert_inference_vars_for_rpit(return_ty, rpits.clone(), fn_placeholders);
814+
let result = self.insert_inference_vars_for_impl_trait(
815+
return_ty,
816+
rpits.clone(),
817+
fn_placeholders,
818+
);
806819
let rpits = rpits.skip_binders();
807820
for (id, _) in rpits.impl_traits.iter() {
808821
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@@ -817,13 +830,19 @@ impl<'a> InferenceContext<'a> {
817830

818831
self.return_ty = self.normalize_associated_types_in(return_ty);
819832
self.return_coercion = Some(CoerceMany::new(self.return_ty.clone()));
833+
834+
// Functions might be associated items that define ATPITs.
835+
// To define an ATPITs, that ATPIT must appear in the function's signatures.
836+
// So, it suffices to check for params and return types.
837+
params_and_ret_tys.push(self.return_ty.clone());
838+
self.insert_atpit_coercion_table(params_and_ret_tys.iter());
820839
}
821840

822-
fn insert_inference_vars_for_rpit<T>(
841+
fn insert_inference_vars_for_impl_trait<T>(
823842
&mut self,
824843
t: T,
825-
rpits: Arc<chalk_ir::Binders<crate::ReturnTypeImplTraits>>,
826-
fn_placeholders: Substitution,
844+
rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
845+
placeholders: Substitution,
827846
) -> T
828847
where
829848
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
@@ -837,22 +856,22 @@ impl<'a> InferenceContext<'a> {
837856
};
838857
let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
839858
ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
859+
ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
840860
_ => unreachable!(),
841861
};
842862
let bounds =
843863
(*rpits).map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
844864
let var = self.table.new_type_var();
845865
let var_subst = Substitution::from1(Interner, var.clone());
846866
for bound in bounds {
847-
let predicate =
848-
bound.map(|it| it.cloned()).substitute(Interner, &fn_placeholders);
867+
let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
849868
let (var_predicate, binders) =
850869
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
851870
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
852-
let var_predicate = self.insert_inference_vars_for_rpit(
871+
let var_predicate = self.insert_inference_vars_for_impl_trait(
853872
var_predicate,
854873
rpits.clone(),
855-
fn_placeholders.clone(),
874+
placeholders.clone(),
856875
);
857876
self.push_obligation(var_predicate.cast(Interner));
858877
}
@@ -863,6 +882,106 @@ impl<'a> InferenceContext<'a> {
863882
)
864883
}
865884

885+
/// The coercion of a non-inference var into an opaque type should fail,
886+
/// but not in the defining sites of the ATPITs.
887+
/// In such cases, we insert an proxy inference var for each ATPIT,
888+
/// and coerce into it instead of ATPIT itself.
889+
///
890+
/// The inference var stretagy is effective because;
891+
///
892+
/// - It can still unify types that coerced into ATPIT
893+
/// - We are pushing `impl Trait` bounds into it
894+
///
895+
/// This function inserts a map that maps the opaque type to that proxy inference var.
896+
fn insert_atpit_coercion_table<'b>(&mut self, tys: impl Iterator<Item = &'b Ty>) {
897+
struct OpaqueTyCollector<'a, 'b> {
898+
table: &'b mut InferenceTable<'a>,
899+
opaque_tys: FxHashMap<OpaqueTyId, Ty>,
900+
}
901+
902+
impl<'a, 'b> TypeVisitor<Interner> for OpaqueTyCollector<'a, 'b> {
903+
type BreakTy = ();
904+
905+
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> {
906+
self
907+
}
908+
909+
fn interner(&self) -> Interner {
910+
Interner
911+
}
912+
913+
fn visit_ty(
914+
&mut self,
915+
ty: &chalk_ir::Ty<Interner>,
916+
outer_binder: DebruijnIndex,
917+
) -> std::ops::ControlFlow<Self::BreakTy> {
918+
let ty = self.table.resolve_ty_shallow(ty);
919+
920+
if let TyKind::OpaqueType(id, _) = ty.kind(Interner) {
921+
self.opaque_tys.insert(*id, ty.clone());
922+
}
923+
924+
ty.super_visit_with(self, outer_binder)
925+
}
926+
}
927+
928+
// Early return if this is not happening inside the impl block
929+
let impl_id = if let Some(impl_id) = self.resolver.impl_def() {
930+
impl_id
931+
} else {
932+
return;
933+
};
934+
935+
let assoc_tys: FxHashSet<_> = self
936+
.db
937+
.impl_data(impl_id)
938+
.items
939+
.iter()
940+
.filter_map(|item| match item {
941+
AssocItemId::TypeAliasId(alias) => Some(*alias),
942+
_ => None,
943+
})
944+
.collect();
945+
if assoc_tys.is_empty() {
946+
return;
947+
}
948+
949+
let mut collector =
950+
OpaqueTyCollector { table: &mut self.table, opaque_tys: FxHashMap::default() };
951+
for ty in tys {
952+
ty.visit_with(collector.as_dyn(), DebruijnIndex::INNERMOST);
953+
}
954+
let atpit_coercion_table: FxHashMap<_, _> = collector
955+
.opaque_tys
956+
.into_iter()
957+
.filter_map(|(opaque_ty_id, ty)| {
958+
if let ImplTraitId::AssociatedTypeImplTrait(alias_id, _) =
959+
self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
960+
{
961+
if assoc_tys.contains(&alias_id) {
962+
let atpits = self
963+
.db
964+
.type_alias_impl_traits(alias_id)
965+
.expect("Marked as ATPIT but no impl traits!");
966+
let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
967+
let ty = self.insert_inference_vars_for_impl_trait(
968+
ty,
969+
atpits,
970+
alias_placeholders,
971+
);
972+
return Some((opaque_ty_id, ty));
973+
}
974+
}
975+
976+
None
977+
})
978+
.collect();
979+
980+
if !atpit_coercion_table.is_empty() {
981+
self.table.atpit_coercion_table = Some(atpit_coercion_table);
982+
}
983+
}
984+
866985
fn infer_body(&mut self) {
867986
match self.return_coercion {
868987
Some(_) => self.infer_return(self.body.body_expr),

crates/hir-ty/src/infer/coerce.rs

+17
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,23 @@ impl InferenceTable<'_> {
276276
return success(simple(Adjust::NeverToAny)(to_ty.clone()), to_ty.clone(), vec![]);
277277
}
278278

279+
// If we are coercing into an ATPIT, coerce into its proxy inference var, instead.
280+
let mut to_ty = to_ty;
281+
let _to;
282+
if let Some(atpit_table) = &self.atpit_coercion_table {
283+
if let TyKind::OpaqueType(opaque_ty_id, _) = to_ty.kind(Interner) {
284+
if !matches!(
285+
from_ty.kind(Interner),
286+
TyKind::InferenceVar(..) | TyKind::OpaqueType(..)
287+
) {
288+
if let Some(ty) = atpit_table.get(opaque_ty_id) {
289+
_to = ty.clone();
290+
to_ty = &_to;
291+
}
292+
}
293+
}
294+
}
295+
279296
// Consider coercing the subtype to a DST
280297
if let Ok(ret) = self.try_coerce_unsized(&from_ty, to_ty) {
281298
return Ok(ret);

0 commit comments

Comments
 (0)