fix: Fix panics on GATs involving const generics

This workaround avoids constant crashing of rust analyzer when using GATs with const generics,
even when the const generics are only on the `impl` block.

The workaround treats GATs as non-existing if either itself or the parent has const generics and
removes relevant panicking code-paths.
This commit is contained in:
Dominik Gschwind 2022-08-16 17:30:17 +02:00
parent b6d59f2bb4
commit ad7a1ed8cc
No known key found for this signature in database
GPG key ID: FD3C82A9C51C4025
3 changed files with 39 additions and 9 deletions

View file

@ -509,7 +509,14 @@ impl<'a> TyLoweringContext<'a> {
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into())) TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
} }
ParamLoweringMode::Variable => { ParamLoweringMode::Variable => {
let idx = generics.param_idx(param_id.into()).expect("matching generics"); let idx = match generics.param_idx(param_id.into()) {
None => {
never!("no matching generics");
return (TyKind::Error.intern(Interner), None);
}
Some(idx) => idx,
};
TyKind::BoundVar(BoundVar::new(self.in_binders, idx)) TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
} }
} }

View file

@ -1526,6 +1526,27 @@ unsafe impl Storage for InlineStorage {
); );
} }
#[test]
fn gat_crash_3() {
cov_mark::check!(ignore_gats);
check_no_mismatches(
r#"
trait Collection {
type Item;
type Member<T>: Collection<Item = T>;
fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
}
struct ConstGen<T, const N: usize> {
data: [T; N],
}
impl<T, const N: usize> Collection for ConstGen<T, N> {
type Item = T;
type Member<U> = ConstGen<U, N>;
}
"#,
);
}
#[test] #[test]
fn cfgd_out_self_param() { fn cfgd_out_self_param() {
cov_mark::check!(cfgd_out_self_param); cov_mark::check!(cfgd_out_self_param);

View file

@ -176,10 +176,16 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def))); let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) { if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
let params = db.generic_params(def); let params = db.generic_params(def);
let parent_params = &parent_generics.as_ref().unwrap().params;
let has_consts = let has_consts =
params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_))); params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
return if has_consts { let parent_has_consts =
// XXX: treat const generic associated types as not existing to avoid crashes (#11769) parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
return if has_consts || parent_has_consts {
// XXX: treat const generic associated types as not existing to avoid crashes
// (#11769, #12193)
// Note: also crashes when the parent has const generics (also even if the GAT
// doesn't use them), see `tests::regression::gat_crash_3` for an example.
// //
// Chalk expects the inner associated type's parameters to come // Chalk expects the inner associated type's parameters to come
// *before*, not after the trait's generics as we've always done it. // *before*, not after the trait's generics as we've always done it.
@ -264,12 +270,8 @@ impl Generics {
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> { fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
if param.parent == self.def { if param.parent == self.def {
let (idx, (_local_id, data)) = self let (idx, (_local_id, data)) =
.params self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
.iter()
.enumerate()
.find(|(_, (idx, _))| *idx == param.local_id)
.unwrap();
let parent_len = self.parent_generics().map_or(0, Generics::len); let parent_len = self.parent_generics().map_or(0, Generics::len);
Some((parent_len + idx, data)) Some((parent_len + idx, data))
} else { } else {