Skip to content

Commit 629fbaf

Browse files
Compute values of RPITITs in impls on a separate query
The previous setup led to a cycle in scenario like the following: ```rust trait Trait { type Assoc; fn foo() -> impl Sized; } impl Trait for () { type Assoc = (); fn foo() -> Self::Assoc; } ``` Because we asked Chalk to normalize the return type of the method, and for that it asked us the datum of the impl, which again causes us to evaluate the RPITITs. Instead, we only put the associated type ID in `impl_datum()`, and we compute it on `associated_ty_value()`. This still causes a cycle because Chalk needlessly evaluates all associated types for an impl, but at least it'll work for the new trait solver, and compiler-errors will try to fix that for Chalk too.
1 parent 945a7c0 commit 629fbaf

File tree

3 files changed

+173
-160
lines changed

3 files changed

+173
-160
lines changed

crates/hir-ty/src/chalk_db.rs

+144-149
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ use hir_def::{
2424
generics::{GenericParams, TypeOrConstParamData},
2525
hir::Movability,
2626
lang_item::{LangItem, LangItemTarget},
27-
nameres::assoc::ImplItems,
2827
};
2928

3029
use crate::{
@@ -915,7 +914,14 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId)
915914
trait_data.associated_type_by_name(name).is_some()
916915
})
917916
.map(to_assoc_type_value_id)
918-
.chain(impl_rpitit_values(db, impl_id, &trait_ref_binders, &impl_items, &trait_datum))
917+
.chain(trait_datum.associated_ty_ids.iter().filter_map(|&trait_assoc| {
918+
match from_assoc_type_id(db, trait_assoc) {
919+
AnyTraitAssocType::Rpitit(trait_assoc) => Some(to_assoc_type_value_id_rpitit(
920+
RpititImplAssocTyId::new(db, RpititImplAssocTy { impl_id, trait_assoc }),
921+
)),
922+
AnyTraitAssocType::Normal(_) => None,
923+
}
924+
}))
919925
.collect();
920926
debug!("impl_datum: {:?}", impl_datum_bound);
921927
let impl_datum = ImplDatum {
@@ -927,157 +933,119 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId)
927933
Arc::new(impl_datum)
928934
}
929935

930-
fn impl_rpitit_values(
936+
// We return a list and not a hasmap because the number of RPITITs in a function should be small.
937+
#[salsa::tracked(return_ref)]
938+
fn impl_method_rpitit_values(
931939
db: &dyn HirDatabase,
932940
impl_id: hir_def::ImplId,
933-
impl_trait: &Binders<TraitRef>,
934-
impl_items: &ImplItems,
935-
trait_datum: &TraitDatum,
936-
) -> impl Iterator<Item = AssociatedTyValueId> {
937-
let mut trait_rpitit_to_impl_rpitit = FxHashMap::default();
938-
939-
return trait_datum
940-
.associated_ty_ids
941-
.iter()
942-
.filter_map(|&trait_assoc| match from_assoc_type_id(db, trait_assoc) {
943-
AnyTraitAssocType::Rpitit(it) => Some(it),
944-
AnyTraitAssocType::Normal(_) => None,
945-
})
946-
.map(move |trait_assoc_id| {
947-
if let Some(&impl_assoc_id) = trait_rpitit_to_impl_rpitit.get(&trait_assoc_id) {
948-
return impl_assoc_id;
941+
trait_method_id: FunctionId,
942+
) -> Box<[Arc<AssociatedTyValue>]> {
943+
let impl_items = db.impl_items(impl_id);
944+
let trait_method_generics = generics(db.upcast(), trait_method_id.into());
945+
let impl_datum =
946+
db.impl_datum(impl_id.loc(db).container.krate(), hir_def::ImplId::to_chalk(impl_id, db));
947+
let trait_method = db.function_data(trait_method_id);
948+
let Some(impl_method) = impl_items.items.iter().find_map(|(name, id)| {
949+
if *name == trait_method.name {
950+
match *id {
951+
AssocItemId::FunctionId(it) => Some(it),
952+
_ => None,
949953
}
954+
} else {
955+
None
956+
}
957+
}) else {
958+
// FIXME: Handle defaulted methods.
959+
return Box::default();
960+
};
950961

951-
let trait_assoc = trait_assoc_id.loc(db);
952-
let trait_method_id = trait_assoc.synthesized_from_method;
953-
let trait_method_generics = generics(db.upcast(), trait_method_id.into());
954-
955-
let impl_assoc_id = (|| {
956-
let trait_method = db.function_data(trait_method_id);
957-
let impl_method = impl_items.items.iter().find_map(|(name, id)| {
958-
if *name == trait_method.name {
959-
match *id {
960-
AssocItemId::FunctionId(it) => Some(it),
961-
_ => None,
962-
}
963-
} else {
964-
None
965-
}
966-
})?;
967-
968-
let impl_method_generics = generics(db.upcast(), impl_method.into());
969-
970-
// First, just so we won't ICE, check that the impl method generics match the trait method generics.
971-
if !check_method_generics_are_structurally_compatible(
972-
trait_method_generics.self_params(),
973-
impl_method_generics.self_params(),
974-
) {
975-
return None;
976-
}
977-
978-
// The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var,
979-
// then we equate the return type of the trait method with the return type of the impl method. The values
980-
// of the inference vars now represent the value of the RPITIT assoc types.
981-
let mut table = InferenceTable::new(db, db.trait_environment(impl_method.into()));
982-
let impl_method_placeholder_subst = impl_method_generics.placeholder_subst(db);
983-
984-
let impl_method_ret = db
985-
.callable_item_signature(impl_method.into())
986-
.substitute(Interner, &impl_method_placeholder_subst)
987-
.ret()
988-
.clone();
989-
let impl_method_ret = table.normalize_associated_types_in(impl_method_ret);
990-
991-
// Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
992-
let trait_ref_placeholder_subst = &impl_method_placeholder_subst.as_slice(Interner)
993-
[impl_method_generics.len_self()..];
994-
// We want to substitute the TraitRef with placeholders, but placeholders from the method, not the impl.
995-
let impl_trait_ref =
996-
impl_trait.clone().substitute(Interner, trait_ref_placeholder_subst);
997-
let trait_to_impl_args = Substitution::from_iter(
998-
Interner,
999-
impl_method_placeholder_subst.as_slice(Interner)
1000-
[..impl_method_generics.len_self()]
1001-
.iter()
1002-
.chain(impl_trait_ref.substitution.as_slice(Interner)),
1003-
);
1004-
let trait_method_ret = db
1005-
.callable_item_signature(trait_method_id.into())
1006-
.substitute(Interner, &trait_to_impl_args)
1007-
.ret()
1008-
.clone();
1009-
let mut rpitit_to_infer_var_folder = RpititToInferVarFolder {
1010-
db,
1011-
table: &mut table,
1012-
trait_method_id,
1013-
trait_rpitit_to_infer_var: FxHashMap::default(),
1014-
};
1015-
let trait_method_ret = trait_method_ret
1016-
.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST);
1017-
let trait_rpitit_to_infer_var =
1018-
rpitit_to_infer_var_folder.trait_rpitit_to_infer_var;
1019-
let trait_method_ret = table.normalize_associated_types_in(trait_method_ret);
1020-
1021-
table.resolve_obligations_as_possible();
1022-
// Even if unification fails, we want to continue. We will fill the RPITITs with error types.
1023-
table.unify(&trait_method_ret, &impl_method_ret);
1024-
table.resolve_obligations_as_possible();
1025-
for (trait_rpitit, infer_var) in trait_rpitit_to_infer_var {
1026-
let impl_rpitit = table.resolve_completely(infer_var);
1027-
let impl_rpitit = impl_rpitit.fold_with(
1028-
&mut PlaceholderToBoundVarFolder {
1029-
db,
1030-
method: impl_method.into(),
1031-
method_generics: impl_method_generics.self_params(),
1032-
},
1033-
DebruijnIndex::INNERMOST,
1034-
);
1035-
let impl_rpitit_binders = VariableKinds::from_iter(
1036-
Interner,
1037-
&trait_assoc.bounds.binders.as_slice(Interner)
1038-
[..trait_method_generics.len()],
1039-
);
1040-
let impl_rpitit = Binders::new(
1041-
impl_rpitit_binders,
1042-
rust_ir::AssociatedTyValueBound { ty: impl_rpitit },
1043-
);
1044-
let impl_rpitit = RpititImplAssocTyId::new(
1045-
db,
1046-
RpititImplAssocTy {
1047-
impl_id,
1048-
trait_assoc: trait_rpitit,
1049-
value: impl_rpitit,
1050-
},
1051-
);
1052-
trait_rpitit_to_impl_rpitit.insert(trait_rpitit, impl_rpitit);
1053-
}
962+
let impl_method_generics = generics(db.upcast(), impl_method.into());
1054963

1055-
trait_rpitit_to_impl_rpitit.get(&trait_assoc_id).copied()
1056-
})();
964+
// First, just so we won't ICE, check that the impl method generics match the trait method generics.
965+
if !check_method_generics_are_structurally_compatible(
966+
trait_method_generics.self_params(),
967+
impl_method_generics.self_params(),
968+
) {
969+
return Box::default();
970+
}
1057971

1058-
impl_assoc_id.unwrap_or_else(|| {
1059-
RpititImplAssocTyId::new(
972+
// The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var,
973+
// then we equate the return type of the trait method with the return type of the impl method. The values
974+
// of the inference vars now represent the value of the RPITIT assoc types.
975+
let mut table = InferenceTable::new(db, db.trait_environment(impl_method.into()));
976+
let impl_method_placeholder_subst = impl_method_generics.placeholder_subst(db);
977+
978+
let impl_method_ret = db
979+
.callable_item_signature(impl_method.into())
980+
.substitute(Interner, &impl_method_placeholder_subst)
981+
.ret()
982+
.clone();
983+
let impl_method_ret = table.normalize_associated_types_in(impl_method_ret);
984+
985+
// Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
986+
let trait_ref_placeholder_subst =
987+
&impl_method_placeholder_subst.as_slice(Interner)[impl_method_generics.len_self()..];
988+
// We want to substitute the TraitRef with placeholders, but placeholders from the method, not the impl.
989+
let impl_trait_ref = impl_datum
990+
.binders
991+
.as_ref()
992+
.map(|it| it.trait_ref.clone())
993+
.substitute(Interner, trait_ref_placeholder_subst);
994+
let trait_to_impl_args = Substitution::from_iter(
995+
Interner,
996+
impl_method_placeholder_subst.as_slice(Interner)[..impl_method_generics.len_self()]
997+
.iter()
998+
.chain(impl_trait_ref.substitution.as_slice(Interner)),
999+
);
1000+
let trait_method_ret = db
1001+
.callable_item_signature(trait_method_id.into())
1002+
.substitute(Interner, &trait_to_impl_args)
1003+
.ret()
1004+
.clone();
1005+
let mut rpitit_to_infer_var_folder = RpititToInferVarFolder {
1006+
db,
1007+
table: &mut table,
1008+
trait_method_id,
1009+
trait_rpitit_to_infer_var: FxHashMap::default(),
1010+
};
1011+
let trait_method_ret =
1012+
trait_method_ret.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST);
1013+
let trait_rpitit_to_infer_var = rpitit_to_infer_var_folder.trait_rpitit_to_infer_var;
1014+
let trait_method_ret = table.normalize_associated_types_in(trait_method_ret);
1015+
1016+
table.resolve_obligations_as_possible();
1017+
// Even if unification fails, we want to continue. We will fill the RPITITs with error types.
1018+
table.unify(&trait_method_ret, &impl_method_ret);
1019+
table.resolve_obligations_as_possible();
1020+
1021+
return trait_rpitit_to_infer_var
1022+
.into_iter()
1023+
.map(|(trait_assoc_id, infer_var)| {
1024+
let impl_rpitit = table.resolve_completely(infer_var);
1025+
let impl_rpitit = impl_rpitit.fold_with(
1026+
&mut PlaceholderToBoundVarFolder {
10601027
db,
1061-
RpititImplAssocTy {
1062-
impl_id,
1063-
trait_assoc: trait_assoc_id,
1064-
// In this situation, we don't know even that the trait and impl generics match, therefore
1065-
// the only binders we can give to comply with the trait's binders are the trait's binders.
1066-
// However, for impl associated types chalk wants only their own generics, excluding
1067-
// those of the impl (unlike in traits), therefore we filter them here.
1068-
value: Binders::new(
1069-
VariableKinds::from_iter(
1070-
Interner,
1071-
&trait_assoc.bounds.binders.as_slice(Interner)
1072-
[..trait_method_generics.len_self()],
1073-
),
1074-
rust_ir::AssociatedTyValueBound { ty: TyKind::Error.intern(Interner) },
1075-
),
1076-
},
1077-
)
1028+
method: impl_method.into(),
1029+
method_generics: impl_method_generics.self_params(),
1030+
},
1031+
DebruijnIndex::INNERMOST,
1032+
);
1033+
let trait_assoc = trait_assoc_id.loc(db);
1034+
let impl_rpitit_binders = VariableKinds::from_iter(
1035+
Interner,
1036+
&trait_assoc.bounds.binders.as_slice(Interner)[..trait_method_generics.len()],
1037+
);
1038+
let impl_rpitit = Binders::new(
1039+
impl_rpitit_binders,
1040+
rust_ir::AssociatedTyValueBound { ty: impl_rpitit },
1041+
);
1042+
Arc::new(AssociatedTyValue {
1043+
associated_ty_id: to_assoc_type_id_rpitit(trait_assoc_id),
1044+
impl_id: hir_def::ImplId::to_chalk(impl_id, db),
1045+
value: impl_rpitit,
10781046
})
10791047
})
1080-
.map(to_assoc_type_value_id_rpitit);
1048+
.collect();
10811049

10821050
#[derive(chalk_derive::FallibleTypeFolder)]
10831051
#[has_interner(Interner)]
@@ -1292,11 +1260,38 @@ pub(crate) fn associated_ty_value_query(
12921260
}
12931261
AnyImplAssocType::Rpitit(assoc_type_id) => {
12941262
let assoc_type = assoc_type_id.loc(db);
1295-
Arc::new(AssociatedTyValue {
1296-
impl_id: assoc_type.impl_id.to_chalk(db),
1297-
associated_ty_id: to_assoc_type_id_rpitit(assoc_type.trait_assoc),
1298-
value: assoc_type.value.clone(),
1299-
})
1263+
let trait_assoc = assoc_type.trait_assoc.loc(db);
1264+
let all_method_assocs = impl_method_rpitit_values(
1265+
db,
1266+
assoc_type.impl_id,
1267+
trait_assoc.synthesized_from_method,
1268+
);
1269+
let trait_assoc_id = to_assoc_type_id_rpitit(assoc_type.trait_assoc);
1270+
all_method_assocs
1271+
.iter()
1272+
.find(|method_assoc| method_assoc.associated_ty_id == trait_assoc_id)
1273+
.cloned()
1274+
.unwrap_or_else(|| {
1275+
let impl_id = hir_def::ImplId::to_chalk(assoc_type.impl_id, db);
1276+
let trait_method_generics =
1277+
generics(db.upcast(), trait_assoc.synthesized_from_method.into());
1278+
Arc::new(AssociatedTyValue {
1279+
associated_ty_id: trait_assoc_id,
1280+
impl_id,
1281+
// In this situation, we don't know even that the trait and impl generics match, therefore
1282+
// the only binders we can give to comply with the trait's binders are the trait's binders.
1283+
// However, for impl associated types chalk wants only their own generics, excluding
1284+
// those of the impl (unlike in traits), therefore we filter them here.
1285+
value: Binders::new(
1286+
VariableKinds::from_iter(
1287+
Interner,
1288+
&trait_assoc.bounds.binders.as_slice(Interner)
1289+
[..trait_method_generics.len_self()],
1290+
),
1291+
rust_ir::AssociatedTyValueBound { ty: TyKind::Error.intern(Interner) },
1292+
),
1293+
})
1294+
})
13001295
}
13011296
}
13021297
}

crates/hir-ty/src/db.rs

-5
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,6 @@ pub struct RpititImplAssocTy {
361361
pub impl_id: ImplId,
362362
/// The definition of this associated type in the trait.
363363
pub trait_assoc: RpititTraitAssocTyId,
364-
/// The bounds of this associated type (coming from the `impl Bounds`).
365-
///
366-
/// The generics are the generics of the method (with some modifications that we
367-
/// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html).
368-
pub value: Binders<chalk_solve::rust_ir::AssociatedTyValueBound<Interner>>,
369364
}
370365

371366
impl_intern_key_ref!(RpititImplAssocTyId, RpititImplAssocTy);

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

+29-6
Original file line numberDiff line numberDiff line change
@@ -4978,17 +4978,18 @@ fn check_rpitit(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
49784978
.iter()
49794979
.copied()
49804980
.filter_map(|assoc_id| {
4981-
let assoc = match from_assoc_type_value_id(&db, assoc_id) {
4982-
AnyImplAssocType::Rpitit(assoc_id) => Some(assoc_id.loc(&db)),
4983-
AnyImplAssocType::Normal(_) => None,
4984-
}?;
4985-
let ty = assoc
4981+
let trait_assoc = match from_assoc_type_value_id(&db, assoc_id) {
4982+
AnyImplAssocType::Rpitit(assoc) => assoc.loc(&db).trait_assoc,
4983+
AnyImplAssocType::Normal(_) => return None,
4984+
};
4985+
let assoc_datum = db.associated_ty_value(test_crate, assoc_id);
4986+
let ty = assoc_datum
49864987
.value
49874988
.skip_binders()
49884989
.ty
49894990
.display_test(&db, DisplayTarget::from_crate(&db, test_crate));
49904991
let method_name = db
4991-
.function_data(assoc.trait_assoc.loc(&db).synthesized_from_method)
4992+
.function_data(trait_assoc.loc(&db).synthesized_from_method)
49924993
.name
49934994
.symbol()
49944995
.clone();
@@ -5067,3 +5068,25 @@ impl<T> Trait for Foo<T> {
50675068
"#]],
50685069
);
50695070
}
5071+
5072+
#[test]
5073+
fn rpitit_referring_self_assoc_type_in_impl_does_not_cycle() {
5074+
check_rpitit(
5075+
r#"
5076+
//- minicore: sized
5077+
trait Trait {
5078+
type Assoc;
5079+
fn foo() -> impl Sized;
5080+
}
5081+
impl Trait for () {
5082+
type Assoc = ();
5083+
fn foo() -> Self::Assoc;
5084+
}
5085+
"#,
5086+
expect![[r#"
5087+
type __foo_rpitit: Sized;
5088+
5089+
type __foo_rpitit = ();
5090+
"#]],
5091+
);
5092+
}

0 commit comments

Comments
 (0)