Skip to content

Commit 0e480cc

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 a609ba6 commit 0e480cc

File tree

4 files changed

+175
-162
lines changed

4 files changed

+175
-162
lines changed

crates/hir-ty/src/chalk_db.rs

Lines changed: 144 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ use hir_def::{
2525
generics::{GenericParams, TypeOrConstParamData},
2626
},
2727
lang_item::LangItem,
28-
nameres::assoc::ImplItems,
2928
signatures::{ImplFlags, StructFlags, TraitFlags},
3029
};
3130

@@ -943,7 +942,14 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId)
943942
trait_data.associated_type_by_name(name).is_some()
944943
})
945944
.map(to_assoc_type_value_id)
946-
.chain(impl_rpitit_values(db, impl_id, &trait_ref_binders, &impl_items, &trait_datum))
945+
.chain(trait_datum.associated_ty_ids.iter().filter_map(|&trait_assoc| {
946+
match from_assoc_type_id(db, trait_assoc) {
947+
AnyTraitAssocType::Rpitit(trait_assoc) => Some(to_assoc_type_value_id_rpitit(
948+
RpititImplAssocTyId::new(db, RpititImplAssocTy { impl_id, trait_assoc }),
949+
)),
950+
AnyTraitAssocType::Normal(_) => None,
951+
}
952+
}))
947953
.collect();
948954
debug!("impl_datum: {:?}", impl_datum_bound);
949955
let impl_datum = ImplDatum {
@@ -955,157 +961,119 @@ fn impl_def_datum(db: &dyn HirDatabase, krate: Crate, impl_id: hir_def::ImplId)
955961
Arc::new(impl_datum)
956962
}
957963

958-
fn impl_rpitit_values(
964+
// We return a list and not a hasmap because the number of RPITITs in a function should be small.
965+
#[salsa_macros::tracked(return_ref)]
966+
fn impl_method_rpitit_values(
959967
db: &dyn HirDatabase,
960968
impl_id: hir_def::ImplId,
961-
impl_trait: &Binders<TraitRef>,
962-
impl_items: &ImplItems,
963-
trait_datum: &TraitDatum,
964-
) -> impl Iterator<Item = AssociatedTyValueId> {
965-
let mut trait_rpitit_to_impl_rpitit = FxHashMap::default();
966-
967-
return trait_datum
968-
.associated_ty_ids
969-
.iter()
970-
.filter_map(|&trait_assoc| match from_assoc_type_id(db, trait_assoc) {
971-
AnyTraitAssocType::Rpitit(it) => Some(it),
972-
AnyTraitAssocType::Normal(_) => None,
973-
})
974-
.map(move |trait_assoc_id| {
975-
if let Some(&impl_assoc_id) = trait_rpitit_to_impl_rpitit.get(&trait_assoc_id) {
976-
return impl_assoc_id;
969+
trait_method_id: FunctionId,
970+
) -> Box<[Arc<AssociatedTyValue>]> {
971+
let impl_items = db.impl_items(impl_id);
972+
let trait_method_generics = generics(db, trait_method_id.into());
973+
let impl_datum =
974+
db.impl_datum(impl_id.loc(db).container.krate(), hir_def::ImplId::to_chalk(impl_id, db));
975+
let trait_method = db.function_signature(trait_method_id);
976+
let Some(impl_method) = impl_items.items.iter().find_map(|(name, id)| {
977+
if *name == trait_method.name {
978+
match *id {
979+
AssocItemId::FunctionId(it) => Some(it),
980+
_ => None,
977981
}
982+
} else {
983+
None
984+
}
985+
}) else {
986+
// FIXME: Handle defaulted methods.
987+
return Box::default();
988+
};
978989

979-
let trait_assoc = trait_assoc_id.loc(db);
980-
let trait_method_id = trait_assoc.synthesized_from_method;
981-
let trait_method_generics = generics(db, trait_method_id.into());
982-
983-
let impl_assoc_id = (|| {
984-
let trait_method = db.function_signature(trait_method_id);
985-
let impl_method = impl_items.items.iter().find_map(|(name, id)| {
986-
if *name == trait_method.name {
987-
match *id {
988-
AssocItemId::FunctionId(it) => Some(it),
989-
_ => None,
990-
}
991-
} else {
992-
None
993-
}
994-
})?;
995-
996-
let impl_method_generics = generics(db, impl_method.into());
997-
998-
// First, just so we won't ICE, check that the impl method generics match the trait method generics.
999-
if !check_method_generics_are_structurally_compatible(
1000-
trait_method_generics.self_params(),
1001-
impl_method_generics.self_params(),
1002-
) {
1003-
return None;
1004-
}
990+
let impl_method_generics = generics(db, impl_method.into());
1005991

1006-
// The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var,
1007-
// then we equate the return type of the trait method with the return type of the impl method. The values
1008-
// of the inference vars now represent the value of the RPITIT assoc types.
1009-
let mut table = InferenceTable::new(db, db.trait_environment(impl_method.into()));
1010-
let impl_method_placeholder_subst = impl_method_generics.placeholder_subst(db);
1011-
1012-
let impl_method_ret = db
1013-
.callable_item_signature(impl_method.into())
1014-
.substitute(Interner, &impl_method_placeholder_subst)
1015-
.ret()
1016-
.clone();
1017-
let impl_method_ret = table.normalize_associated_types_in(impl_method_ret);
1018-
1019-
// Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
1020-
let trait_ref_placeholder_subst = &impl_method_placeholder_subst.as_slice(Interner)
1021-
[impl_method_generics.len_self()..];
1022-
// We want to substitute the TraitRef with placeholders, but placeholders from the method, not the impl.
1023-
let impl_trait_ref =
1024-
impl_trait.clone().substitute(Interner, trait_ref_placeholder_subst);
1025-
let trait_to_impl_args = Substitution::from_iter(
1026-
Interner,
1027-
impl_method_placeholder_subst.as_slice(Interner)
1028-
[..impl_method_generics.len_self()]
1029-
.iter()
1030-
.chain(impl_trait_ref.substitution.as_slice(Interner)),
1031-
);
1032-
let trait_method_ret = db
1033-
.callable_item_signature(trait_method_id.into())
1034-
.substitute(Interner, &trait_to_impl_args)
1035-
.ret()
1036-
.clone();
1037-
let mut rpitit_to_infer_var_folder = RpititToInferVarFolder {
1038-
db,
1039-
table: &mut table,
1040-
trait_method_id,
1041-
trait_rpitit_to_infer_var: FxHashMap::default(),
1042-
};
1043-
let trait_method_ret = trait_method_ret
1044-
.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST);
1045-
let trait_rpitit_to_infer_var =
1046-
rpitit_to_infer_var_folder.trait_rpitit_to_infer_var;
1047-
let trait_method_ret = table.normalize_associated_types_in(trait_method_ret);
1048-
1049-
table.resolve_obligations_as_possible();
1050-
// Even if unification fails, we want to continue. We will fill the RPITITs with error types.
1051-
table.unify(&trait_method_ret, &impl_method_ret);
1052-
table.resolve_obligations_as_possible();
1053-
for (trait_rpitit, infer_var) in trait_rpitit_to_infer_var {
1054-
let impl_rpitit = table.resolve_completely(infer_var);
1055-
let impl_rpitit = impl_rpitit.fold_with(
1056-
&mut PlaceholderToBoundVarFolder {
1057-
db,
1058-
method: impl_method.into(),
1059-
method_generics: impl_method_generics.self_params(),
1060-
},
1061-
DebruijnIndex::INNERMOST,
1062-
);
1063-
let impl_rpitit_binders = VariableKinds::from_iter(
1064-
Interner,
1065-
&trait_assoc.bounds.binders.as_slice(Interner)
1066-
[..trait_method_generics.len()],
1067-
);
1068-
let impl_rpitit = Binders::new(
1069-
impl_rpitit_binders,
1070-
rust_ir::AssociatedTyValueBound { ty: impl_rpitit },
1071-
);
1072-
let impl_rpitit = RpititImplAssocTyId::new(
1073-
db,
1074-
RpititImplAssocTy {
1075-
impl_id,
1076-
trait_assoc: trait_rpitit,
1077-
value: impl_rpitit,
1078-
},
1079-
);
1080-
trait_rpitit_to_impl_rpitit.insert(trait_rpitit, impl_rpitit);
1081-
}
1082-
1083-
trait_rpitit_to_impl_rpitit.get(&trait_assoc_id).copied()
1084-
})();
992+
// First, just so we won't ICE, check that the impl method generics match the trait method generics.
993+
if !check_method_generics_are_structurally_compatible(
994+
trait_method_generics.self_params(),
995+
impl_method_generics.self_params(),
996+
) {
997+
return Box::default();
998+
}
1085999

1086-
impl_assoc_id.unwrap_or_else(|| {
1087-
RpititImplAssocTyId::new(
1000+
// The inference algorithm works as follows: in the trait method, we replace each RPITIT with an infer var,
1001+
// then we equate the return type of the trait method with the return type of the impl method. The values
1002+
// of the inference vars now represent the value of the RPITIT assoc types.
1003+
let mut table = InferenceTable::new(db, db.trait_environment(impl_method.into()));
1004+
let impl_method_placeholder_subst = impl_method_generics.placeholder_subst(db);
1005+
1006+
let impl_method_ret = db
1007+
.callable_item_signature(impl_method.into())
1008+
.substitute(Interner, &impl_method_placeholder_subst)
1009+
.ret()
1010+
.clone();
1011+
let impl_method_ret = table.normalize_associated_types_in(impl_method_ret);
1012+
1013+
// Create mapping from trait to impl (i.e. impl trait header + impl method identity args).
1014+
let trait_ref_placeholder_subst =
1015+
&impl_method_placeholder_subst.as_slice(Interner)[impl_method_generics.len_self()..];
1016+
// We want to substitute the TraitRef with placeholders, but placeholders from the method, not the impl.
1017+
let impl_trait_ref = impl_datum
1018+
.binders
1019+
.as_ref()
1020+
.map(|it| it.trait_ref.clone())
1021+
.substitute(Interner, trait_ref_placeholder_subst);
1022+
let trait_to_impl_args = Substitution::from_iter(
1023+
Interner,
1024+
impl_method_placeholder_subst.as_slice(Interner)[..impl_method_generics.len_self()]
1025+
.iter()
1026+
.chain(impl_trait_ref.substitution.as_slice(Interner)),
1027+
);
1028+
let trait_method_ret = db
1029+
.callable_item_signature(trait_method_id.into())
1030+
.substitute(Interner, &trait_to_impl_args)
1031+
.ret()
1032+
.clone();
1033+
let mut rpitit_to_infer_var_folder = RpititToInferVarFolder {
1034+
db,
1035+
table: &mut table,
1036+
trait_method_id,
1037+
trait_rpitit_to_infer_var: FxHashMap::default(),
1038+
};
1039+
let trait_method_ret =
1040+
trait_method_ret.fold_with(&mut rpitit_to_infer_var_folder, DebruijnIndex::INNERMOST);
1041+
let trait_rpitit_to_infer_var = rpitit_to_infer_var_folder.trait_rpitit_to_infer_var;
1042+
let trait_method_ret = table.normalize_associated_types_in(trait_method_ret);
1043+
1044+
table.resolve_obligations_as_possible();
1045+
// Even if unification fails, we want to continue. We will fill the RPITITs with error types.
1046+
table.unify(&trait_method_ret, &impl_method_ret);
1047+
table.resolve_obligations_as_possible();
1048+
1049+
return trait_rpitit_to_infer_var
1050+
.into_iter()
1051+
.map(|(trait_assoc_id, infer_var)| {
1052+
let impl_rpitit = table.resolve_completely(infer_var);
1053+
let impl_rpitit = impl_rpitit.fold_with(
1054+
&mut PlaceholderToBoundVarFolder {
10881055
db,
1089-
RpititImplAssocTy {
1090-
impl_id,
1091-
trait_assoc: trait_assoc_id,
1092-
// In this situation, we don't know even that the trait and impl generics match, therefore
1093-
// the only binders we can give to comply with the trait's binders are the trait's binders.
1094-
// However, for impl associated types chalk wants only their own generics, excluding
1095-
// those of the impl (unlike in traits), therefore we filter them here.
1096-
value: Binders::new(
1097-
VariableKinds::from_iter(
1098-
Interner,
1099-
&trait_assoc.bounds.binders.as_slice(Interner)
1100-
[..trait_method_generics.len_self()],
1101-
),
1102-
rust_ir::AssociatedTyValueBound { ty: TyKind::Error.intern(Interner) },
1103-
),
1104-
},
1105-
)
1056+
method: impl_method.into(),
1057+
method_generics: impl_method_generics.self_params(),
1058+
},
1059+
DebruijnIndex::INNERMOST,
1060+
);
1061+
let trait_assoc = trait_assoc_id.loc(db);
1062+
let impl_rpitit_binders = VariableKinds::from_iter(
1063+
Interner,
1064+
&trait_assoc.bounds.binders.as_slice(Interner)[..trait_method_generics.len()],
1065+
);
1066+
let impl_rpitit = Binders::new(
1067+
impl_rpitit_binders,
1068+
rust_ir::AssociatedTyValueBound { ty: impl_rpitit },
1069+
);
1070+
Arc::new(AssociatedTyValue {
1071+
associated_ty_id: to_assoc_type_id_rpitit(trait_assoc_id),
1072+
impl_id: hir_def::ImplId::to_chalk(impl_id, db),
1073+
value: impl_rpitit,
11061074
})
11071075
})
1108-
.map(to_assoc_type_value_id_rpitit);
1076+
.collect();
11091077

11101078
#[derive(chalk_derive::FallibleTypeFolder)]
11111079
#[has_interner(Interner)]
@@ -1322,11 +1290,38 @@ pub(crate) fn associated_ty_value_query(
13221290
}
13231291
AnyImplAssocType::Rpitit(assoc_type_id) => {
13241292
let assoc_type = assoc_type_id.loc(db);
1325-
Arc::new(AssociatedTyValue {
1326-
impl_id: assoc_type.impl_id.to_chalk(db),
1327-
associated_ty_id: to_assoc_type_id_rpitit(assoc_type.trait_assoc),
1328-
value: assoc_type.value.clone(),
1329-
})
1293+
let trait_assoc = assoc_type.trait_assoc.loc(db);
1294+
let all_method_assocs = impl_method_rpitit_values(
1295+
db,
1296+
assoc_type.impl_id,
1297+
trait_assoc.synthesized_from_method,
1298+
);
1299+
let trait_assoc_id = to_assoc_type_id_rpitit(assoc_type.trait_assoc);
1300+
all_method_assocs
1301+
.iter()
1302+
.find(|method_assoc| method_assoc.associated_ty_id == trait_assoc_id)
1303+
.cloned()
1304+
.unwrap_or_else(|| {
1305+
let impl_id = hir_def::ImplId::to_chalk(assoc_type.impl_id, db);
1306+
let trait_method_generics =
1307+
generics(db, trait_assoc.synthesized_from_method.into());
1308+
Arc::new(AssociatedTyValue {
1309+
associated_ty_id: trait_assoc_id,
1310+
impl_id,
1311+
// In this situation, we don't know even that the trait and impl generics match, therefore
1312+
// the only binders we can give to comply with the trait's binders are the trait's binders.
1313+
// However, for impl associated types chalk wants only their own generics, excluding
1314+
// those of the impl (unlike in traits), therefore we filter them here.
1315+
value: Binders::new(
1316+
VariableKinds::from_iter(
1317+
Interner,
1318+
&trait_assoc.bounds.binders.as_slice(Interner)
1319+
[..trait_method_generics.len_self()],
1320+
),
1321+
rust_ir::AssociatedTyValueBound { ty: TyKind::Error.intern(Interner) },
1322+
),
1323+
})
1324+
})
13301325
}
13311326
}
13321327
}

crates/hir-ty/src/db.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,11 +373,6 @@ pub struct RpititImplAssocTy {
373373
pub impl_id: ImplId,
374374
/// The definition of this associated type in the trait.
375375
pub trait_assoc: RpititTraitAssocTyId,
376-
/// The bounds of this associated type (coming from the `impl Bounds`).
377-
///
378-
/// The generics are the generics of the method (with some modifications that we
379-
/// don't currently implement, see https://rustc-dev-guide.rust-lang.org/return-position-impl-trait-in-trait.html).
380-
pub value: Binders<chalk_solve::rust_ir::AssociatedTyValueBound<Interner>>,
381376
}
382377

383378
impl_intern_key_ref!(RpititImplAssocTyId, RpititImplAssocTy);

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

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4997,17 +4997,18 @@ fn check_rpitit(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect)
49974997
.iter()
49984998
.copied()
49994999
.filter_map(|assoc_id| {
5000-
let assoc = match from_assoc_type_value_id(&db, assoc_id) {
5001-
AnyImplAssocType::Rpitit(assoc_id) => Some(assoc_id.loc(&db)),
5002-
AnyImplAssocType::Normal(_) => None,
5003-
}?;
5004-
let ty = assoc
5000+
let trait_assoc = match from_assoc_type_value_id(&db, assoc_id) {
5001+
AnyImplAssocType::Rpitit(assoc) => assoc.loc(&db).trait_assoc,
5002+
AnyImplAssocType::Normal(_) => return None,
5003+
};
5004+
let assoc_datum = db.associated_ty_value(test_crate, assoc_id);
5005+
let ty = assoc_datum
50055006
.value
50065007
.skip_binders()
50075008
.ty
50085009
.display_test(&db, DisplayTarget::from_crate(&db, test_crate));
50095010
let method_name = db
5010-
.function_signature(assoc.trait_assoc.loc(&db).synthesized_from_method)
5011+
.function_signature(trait_assoc.loc(&db).synthesized_from_method)
50115012
.name
50125013
.symbol()
50135014
.clone();
@@ -5086,3 +5087,25 @@ impl<T> Trait for Foo<T> {
50865087
"#]],
50875088
);
50885089
}
5090+
5091+
#[test]
5092+
fn rpitit_referring_self_assoc_type_in_impl_does_not_cycle() {
5093+
check_rpitit(
5094+
r#"
5095+
//- minicore: sized
5096+
trait Trait {
5097+
type Assoc;
5098+
fn foo() -> impl Sized;
5099+
}
5100+
impl Trait for () {
5101+
type Assoc = ();
5102+
fn foo() -> Self::Assoc;
5103+
}
5104+
"#,
5105+
expect![[r#"
5106+
type __foo_rpitit: Sized;
5107+
5108+
type __foo_rpitit = ();
5109+
"#]],
5110+
);
5111+
}

0 commit comments

Comments
 (0)