Skip to content

Commit ad7a1ed

Browse files
committed
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.
1 parent b6d59f2 commit ad7a1ed

File tree

3 files changed

+39
-9
lines changed

3 files changed

+39
-9
lines changed

crates/hir-ty/src/lower.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,14 @@ impl<'a> TyLoweringContext<'a> {
509509
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
510510
}
511511
ParamLoweringMode::Variable => {
512-
let idx = generics.param_idx(param_id.into()).expect("matching generics");
512+
let idx = match generics.param_idx(param_id.into()) {
513+
None => {
514+
never!("no matching generics");
515+
return (TyKind::Error.intern(Interner), None);
516+
}
517+
Some(idx) => idx,
518+
};
519+
513520
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
514521
}
515522
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1526,6 +1526,27 @@ unsafe impl Storage for InlineStorage {
15261526
);
15271527
}
15281528

1529+
#[test]
1530+
fn gat_crash_3() {
1531+
cov_mark::check!(ignore_gats);
1532+
check_no_mismatches(
1533+
r#"
1534+
trait Collection {
1535+
type Item;
1536+
type Member<T>: Collection<Item = T>;
1537+
fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
1538+
}
1539+
struct ConstGen<T, const N: usize> {
1540+
data: [T; N],
1541+
}
1542+
impl<T, const N: usize> Collection for ConstGen<T, N> {
1543+
type Item = T;
1544+
type Member<U> = ConstGen<U, N>;
1545+
}
1546+
"#,
1547+
);
1548+
}
1549+
15291550
#[test]
15301551
fn cfgd_out_self_param() {
15311552
cov_mark::check!(cfgd_out_self_param);

crates/hir-ty/src/utils.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,16 @@ pub(crate) fn generics(db: &dyn DefDatabase, def: GenericDefId) -> Generics {
176176
let parent_generics = parent_generic_def(db, def).map(|def| Box::new(generics(db, def)));
177177
if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
178178
let params = db.generic_params(def);
179+
let parent_params = &parent_generics.as_ref().unwrap().params;
179180
let has_consts =
180181
params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
181-
return if has_consts {
182-
// XXX: treat const generic associated types as not existing to avoid crashes (#11769)
182+
let parent_has_consts =
183+
parent_params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
184+
return if has_consts || parent_has_consts {
185+
// XXX: treat const generic associated types as not existing to avoid crashes
186+
// (#11769, #12193)
187+
// Note: also crashes when the parent has const generics (also even if the GAT
188+
// doesn't use them), see `tests::regression::gat_crash_3` for an example.
183189
//
184190
// Chalk expects the inner associated type's parameters to come
185191
// *before*, not after the trait's generics as we've always done it.
@@ -264,12 +270,8 @@ impl Generics {
264270

265271
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
266272
if param.parent == self.def {
267-
let (idx, (_local_id, data)) = self
268-
.params
269-
.iter()
270-
.enumerate()
271-
.find(|(_, (idx, _))| *idx == param.local_id)
272-
.unwrap();
273+
let (idx, (_local_id, data)) =
274+
self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
273275
let parent_len = self.parent_generics().map_or(0, Generics::len);
274276
Some((parent_len + idx, data))
275277
} else {

0 commit comments

Comments
 (0)