Skip to content

Commit fbdcafa

Browse files
Handle RPITIT when inferring the body of the method
Inside the method, we should insert an environment clause `RpititAssoc = Ty` so we know the value of the associated type. This matters mostly for defaulted trait methods, because for them without this calling the same method from the same trait will return the RPITIT and not the opaque type, but it also matters for impl methods, and will matter even more once we start reporting trait errors, as then RTN bounds could cause errors on the methods without this. Unfortunately that means we have to separate `trait_environment()` from `trait_environment_for_body()` (i.e. make the latter no longer a transparent wrapper around the former but a real query), because the RPITIT infra needs to call `trait_environment()` but `trait_environment_for_body()` needs to call it for the `AliasEq` clauses. This means another query, more memory usage etc.. But looks like it's unavoidable.
1 parent a8d89ae commit fbdcafa

File tree

5 files changed

+202
-33
lines changed

5 files changed

+202
-33
lines changed

crates/hir-ty/src/db.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
187187
fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates;
188188

189189
#[salsa::invoke(crate::lower::trait_environment_for_body_query)]
190-
#[salsa::transparent]
191190
fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc<TraitEnvironment>;
192191

193192
#[salsa::invoke(crate::lower::trait_environment_query)]

crates/hir-ty/src/generics.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ impl Generics {
8888
chain!(trait_self_param, toc)
8989
}
9090

91+
pub(crate) fn iter_self_type_or_consts_id(
92+
&self,
93+
) -> impl DoubleEndedIterator<Item = (TypeOrConstParamId, &TypeOrConstParamData)> + '_ {
94+
self.params
95+
.iter_type_or_consts()
96+
.map(|(local_id, data)| (TypeOrConstParamId { parent: self.def, local_id }, data))
97+
}
98+
9199
/// Iterate over the parent params followed by self params.
92100
pub(crate) fn iter(
93101
&self,

crates/hir-ty/src/lower.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use either::Either;
2727
use hir_def::{
2828
AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
2929
FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, ItemContainerId, LocalFieldId,
30-
Lookup, StaticId, StructId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
30+
Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, VariantId,
3131
builtin_type::BuiltinType,
3232
expr_store::{ExpressionStore, path::Path},
3333
hir::generics::{GenericParamDataRef, TypeOrConstParamData, WherePredicate},
@@ -64,7 +64,7 @@ use crate::{
6464
},
6565
make_binders,
6666
mapping::{ToChalk, from_chalk_trait_id, lt_to_placeholder_idx, to_assoc_type_id_rpitit},
67-
rpitit::{RpititTraitAssocTy, RpititTraitAssocTyId},
67+
rpitit::{RpititTraitAssocTy, RpititTraitAssocTyId, add_method_body_rpitit_clauses},
6868
static_lifetime, to_chalk_trait_id, to_placeholder_idx,
6969
utils::all_super_trait_refs,
7070
variable_kinds_from_generics,
@@ -1180,14 +1180,33 @@ pub(crate) fn trait_environment_for_body_query(
11801180
let krate = def.module(db).krate();
11811181
return TraitEnvironment::empty(krate);
11821182
};
1183-
db.trait_environment(def)
1183+
1184+
let generics = generics(db, def);
1185+
let (resolver, traits_in_scope, mut clauses) = trait_environment_shared(db, def, &generics);
1186+
1187+
if let GenericDefId::FunctionId(function) = def {
1188+
add_method_body_rpitit_clauses(db, &generics, &mut clauses, function);
1189+
}
1190+
1191+
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
1192+
TraitEnvironment::new(resolver, None, traits_in_scope.into_boxed_slice(), env)
11841193
}
11851194

11861195
pub(crate) fn trait_environment_query(
11871196
db: &dyn HirDatabase,
11881197
def: GenericDefId,
11891198
) -> Arc<TraitEnvironment> {
11901199
let generics = generics(db, def);
1200+
let (resolver, traits_in_scope, clauses) = trait_environment_shared(db, def, &generics);
1201+
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
1202+
TraitEnvironment::new(resolver, None, traits_in_scope.into_boxed_slice(), env)
1203+
}
1204+
1205+
fn trait_environment_shared(
1206+
db: &dyn HirDatabase,
1207+
def: GenericDefId,
1208+
generics: &Generics,
1209+
) -> (Crate, Vec<(Ty, TraitId)>, Vec<ProgramClause>) {
11911210
let resolver = def.resolver(db);
11921211
let mut ctx = TyLoweringContext::new(
11931212
db,
@@ -1200,7 +1219,7 @@ pub(crate) fn trait_environment_query(
12001219
let mut traits_in_scope = Vec::new();
12011220
let mut clauses = Vec::new();
12021221
for maybe_parent_generics in
1203-
std::iter::successors(Some(&generics), |generics| generics.parent_generics())
1222+
std::iter::successors(Some(generics), |generics| generics.parent_generics())
12041223
{
12051224
ctx.store = maybe_parent_generics.store();
12061225
for pred in maybe_parent_generics.where_predicates() {
@@ -1240,9 +1259,7 @@ pub(crate) fn trait_environment_query(
12401259
};
12411260
}
12421261

1243-
let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses);
1244-
1245-
TraitEnvironment::new(resolver.krate(), None, traits_in_scope.into_boxed_slice(), env)
1262+
(resolver.krate(), traits_in_scope, clauses)
12461263
}
12471264

12481265
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

crates/hir-ty/src/rpitit.rs

Lines changed: 143 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,26 @@ use chalk_ir::{
1010
};
1111
use chalk_solve::rust_ir::AssociatedTyValueBound;
1212
use hir_def::{
13-
AssocItemId, FunctionId, GenericDefId, GenericParamId, ImplId, TraitId,
13+
AssocItemId, ConstParamId, FunctionId, GenericDefId, GenericParamId, ImplId, ItemContainerId,
14+
TraitId,
1415
hir::generics::{GenericParams, TypeOrConstParamData},
1516
resolver::HasResolver,
1617
};
1718
use rustc_hash::FxHashMap;
1819
use thin_vec::ThinVec;
1920

2021
use crate::{
21-
AliasTy, AnyTraitAssocType, Binders, Const, ConstData, ConstValue, DomainGoal, Goal, GoalData,
22-
ImplTraitLoweringMode, InferenceTable, Interner, Lifetime, LifetimeData, LifetimeElisionKind,
23-
ParamLoweringMode, PlaceholderIndex, ProjectionTy, Substitution, TraitRef, Ty, TyKind,
24-
TyLoweringContext, VariableKinds,
22+
AliasEq, AliasTy, AnyTraitAssocType, Binders, Const, ConstData, ConstValue, DomainGoal, Goal,
23+
GoalData, ImplTraitLoweringMode, InferenceTable, Interner, Lifetime, LifetimeData,
24+
LifetimeElisionKind, ParamLoweringMode, PlaceholderIndex, ProgramClause, ProjectionTy,
25+
Substitution, TraitRef, Ty, TyKind, TyLoweringContext, VariableKinds, WhereClause,
2526
chalk_db::{AssociatedTyValue, inline_bound_to_generic_predicate},
2627
db::HirDatabase,
27-
from_assoc_type_id, from_placeholder_idx,
28+
error_lifetime, from_assoc_type_id, from_chalk_trait_id, from_placeholder_idx,
2829
generics::{Generics, generics},
2930
lt_from_placeholder_idx,
3031
mapping::{ToChalk, to_assoc_type_id_rpitit},
31-
variable_kinds_from_generics,
32+
to_placeholder_idx, variable_kinds_from_generics,
3233
};
3334

3435
/// An associated type synthesized from a Return Position Impl Trait In Trait
@@ -72,7 +73,7 @@ pub(crate) fn impl_method_rpitit_values(
7273
db: &dyn HirDatabase,
7374
impl_id: ImplId,
7475
trait_method_id: FunctionId,
75-
) -> Box<[Arc<AssociatedTyValue>]> {
76+
) -> ThinVec<Arc<AssociatedTyValue>> {
7677
let impl_items = db.impl_items(impl_id);
7778
let trait_method_generics = generics(db, trait_method_id.into());
7879
let trait_method = db.function_signature(trait_method_id);
@@ -108,7 +109,7 @@ pub(crate) fn impl_method_rpitit_values(
108109
trait_method_generics.self_params(),
109110
impl_method_generics.self_params(),
110111
) {
111-
return Box::default();
112+
return ThinVec::new();
112113
}
113114

114115
// The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var,
@@ -180,10 +181,12 @@ pub(crate) fn impl_method_rpitit_values(
180181
// generics in the binder.
181182
let impl_rpitit_binders = VariableKinds::from_iter(
182183
Interner,
183-
trait_assoc.bounds.binders.as_slice(Interner)[..trait_method_generics.len()]
184-
.iter()
185-
.cloned()
186-
.chain(variable_kinds_from_generics(db, impl_method_generics.iter_parent_id())),
184+
variable_kinds_from_generics(db, impl_method_generics.iter_parent_id()).chain(
185+
trait_assoc.bounds.binders.as_slice(Interner)
186+
[trait_method_generics.len_parent()..]
187+
.iter()
188+
.cloned(),
189+
),
187190
);
188191
let impl_rpitit =
189192
Binders::new(impl_rpitit_binders, AssociatedTyValueBound { ty: impl_rpitit });
@@ -202,7 +205,7 @@ fn defaulted_impl_method_rpitit_values(
202205
trait_method_id: FunctionId,
203206
impl_trait_ref: Binders<TraitRef>,
204207
trait_method_generics: &Generics,
205-
) -> Box<[Arc<AssociatedTyValue>]> {
208+
) -> ThinVec<Arc<AssociatedTyValue>> {
206209
let defaulted_rpitit_values = defaulted_trait_method_rpitit_values(db, trait_method_id);
207210
let impl_generics = generics(db, impl_id.into());
208211
// The associated type generics as the same as the trait method's, but we take the impl as
@@ -461,12 +464,14 @@ impl TypeFolder<Interner> for PlaceholderToBoundVarFolder<'_> {
461464
)
462465
.to_ty(Interner)
463466
} else if placeholder.parent == self.parent {
464-
BoundVar::new(
465-
DebruijnIndex::INNERMOST,
466-
placeholder.local_id.into_raw().into_u32() as usize
467-
+ self.parent_generics.len_lifetimes(),
468-
)
469-
.to_ty(Interner)
467+
let local_id = placeholder.local_id.into_raw().into_u32();
468+
let index = if matches!(self.parent, GenericDefId::TraitId(_)) && local_id == 0 {
469+
// `Self` parameter.
470+
0
471+
} else {
472+
local_id as usize + self.parent_generics.len_lifetimes()
473+
};
474+
BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(Interner)
470475
} else {
471476
TyKind::Placeholder(universe).intern(Interner)
472477
}
@@ -512,13 +517,126 @@ impl TypeFolder<Interner> for PlaceholderToBoundVarFolder<'_> {
512517
)
513518
.to_lifetime(Interner)
514519
} else if placeholder.parent == self.parent {
515-
BoundVar::new(
516-
DebruijnIndex::INNERMOST,
517-
placeholder.local_id.into_raw().into_u32() as usize,
518-
)
519-
.to_lifetime(Interner)
520+
let local_id = placeholder.local_id.into_raw().into_u32() as usize;
521+
let index = if matches!(self.parent, GenericDefId::TraitId(_)) {
522+
// Account for `Self` parameter that comes before lifetimes.
523+
local_id + 1
524+
} else {
525+
local_id
526+
};
527+
BoundVar::new(DebruijnIndex::INNERMOST, index).to_lifetime(Interner)
520528
} else {
521529
Lifetime::new(Interner, LifetimeData::Placeholder(universe))
522530
}
523531
}
524532
}
533+
534+
/// When inferring a method body of a trait or impl, and that method has RPITITs, we need to add
535+
/// `RpititGeneratedAssoc = Type` clauses.
536+
pub(crate) fn add_method_body_rpitit_clauses(
537+
db: &dyn HirDatabase,
538+
impl_method_generics: &Generics,
539+
clauses: &mut Vec<ProgramClause>,
540+
impl_method: FunctionId,
541+
) {
542+
match impl_method.loc(db).container {
543+
ItemContainerId::ImplId(impl_id) => {
544+
(|| {
545+
let method_data = db.function_signature(impl_method);
546+
let trait_ref = db.impl_trait(impl_id)?;
547+
let trait_items =
548+
db.trait_items(from_chalk_trait_id(trait_ref.skip_binders().trait_id));
549+
let trait_method = trait_items.method_by_name(&method_data.name)?;
550+
551+
let rpitits = impl_method_rpitit_values(db, impl_id, trait_method);
552+
let mut substitution = None;
553+
clauses.extend(rpitits.iter().map(|rpitit| {
554+
let (impl_subst, trait_subst) = substitution.get_or_insert_with(|| {
555+
let impl_method_subst = impl_method_generics.placeholder_subst(db);
556+
let trait_method_generics =
557+
crate::generics::generics(db, trait_method.into());
558+
let trait_method_subst = trait_method_generics.placeholder_subst(db);
559+
let impl_subst = Substitution::from_iter(
560+
Interner,
561+
impl_method_subst.as_slice(Interner)
562+
[..impl_method_generics.len_parent()]
563+
.iter()
564+
.chain(
565+
&trait_method_subst.as_slice(Interner)
566+
[trait_method_generics.len_parent()..],
567+
),
568+
);
569+
570+
let trait_ref_subst =
571+
trait_ref.clone().substitute(Interner, &impl_method_subst);
572+
// Lifetime parameters may change between trait and impl, and we don't check from that in `impl_method_rpitit_values()`
573+
// (because it's valid). So fill them with errors.
574+
// FIXME: This isn't really correct, we should still fill the lifetimes. rustc does some kind of mapping, I think there
575+
// are also restrictions on what exactly lifetimes can change between trait and impl.
576+
let trait_method_subst = std::iter::repeat_n(
577+
error_lifetime().cast(Interner),
578+
trait_method_generics.len_lifetimes_self(),
579+
)
580+
.chain(
581+
impl_method_generics.iter_self_type_or_consts_id().map(
582+
|(param_id, param_data)| {
583+
let placeholder = to_placeholder_idx(db, param_id);
584+
match param_data {
585+
TypeOrConstParamData::TypeParamData(_) => {
586+
placeholder.to_ty(Interner).cast(Interner)
587+
}
588+
TypeOrConstParamData::ConstParamData(_) => placeholder
589+
.to_const(
590+
Interner,
591+
db.const_param_ty(ConstParamId::from_unchecked(
592+
param_id,
593+
)),
594+
)
595+
.cast(Interner),
596+
}
597+
},
598+
),
599+
);
600+
let trait_subst = Substitution::from_iter(
601+
Interner,
602+
trait_ref_subst
603+
.substitution
604+
.iter(Interner)
605+
.cloned()
606+
.chain(trait_method_subst),
607+
);
608+
609+
(impl_subst, trait_subst)
610+
});
611+
WhereClause::AliasEq(AliasEq {
612+
alias: AliasTy::Projection(ProjectionTy {
613+
associated_ty_id: rpitit.associated_ty_id,
614+
substitution: trait_subst.clone(),
615+
}),
616+
ty: rpitit.value.clone().substitute(Interner, &*impl_subst).ty,
617+
})
618+
.cast(Interner)
619+
}));
620+
621+
Some(())
622+
})();
623+
}
624+
ItemContainerId::TraitId(_) => {
625+
let rpitits = defaulted_trait_method_rpitit_values(db, impl_method);
626+
let mut substitution = None;
627+
clauses.extend(rpitits.iter().map(|(trait_rpitit, rpitit_value)| {
628+
let substitution =
629+
substitution.get_or_insert_with(|| impl_method_generics.placeholder_subst(db));
630+
WhereClause::AliasEq(AliasEq {
631+
alias: AliasTy::Projection(ProjectionTy {
632+
associated_ty_id: to_assoc_type_id_rpitit(*trait_rpitit),
633+
substitution: substitution.clone(),
634+
}),
635+
ty: rpitit_value.clone().substitute(Interner, &*substitution),
636+
})
637+
.cast(Interner)
638+
}));
639+
}
640+
_ => {}
641+
}
642+
}

crates/hir-ty/src/tests/traits.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5163,3 +5163,30 @@ impl DesugaredAsyncTrait for () {}
51635163
"#]],
51645164
);
51655165
}
5166+
5167+
#[test]
5168+
fn rpitit_method_body() {
5169+
// This test checks that when inferring a defaulted/impl method with RPITITs, we tell Chalk the generated
5170+
// RPITITs assoc types have values.
5171+
// The expectation should not be (the assoc type) `__foo_rpitit` but (the opaque) `impl Trait`.
5172+
check_infer(
5173+
r#"
5174+
//- minicore: sized
5175+
trait Trait {
5176+
fn foo(&self) -> impl Trait {
5177+
let _ = self.foo();
5178+
loop {}
5179+
}
5180+
}
5181+
"#,
5182+
expect![[r#"
5183+
26..30 'self': &'? Self
5184+
46..97 '{ ... }': !
5185+
60..61 '_': impl Trait
5186+
64..68 'self': &'? Self
5187+
64..74 'self.foo()': impl Trait
5188+
84..91 'loop {}': !
5189+
89..91 '{}': ()
5190+
"#]],
5191+
);
5192+
}

0 commit comments

Comments
 (0)