Skip to content

Commit fdc28b4

Browse files
committed
Auto merge of rust-lang#13021 - N3xed:fix-gat-panics, r=flodiebold
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. Fixes rust-lang#11989, fixes rust-lang#12193
2 parents c2310a0 + ac8cb8c commit fdc28b4

File tree

3 files changed

+49
-9
lines changed

3 files changed

+49
-9
lines changed

crates/hir-ty/src/lower.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,14 @@ impl<'a> TyLoweringContext<'a> {
479479
TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
480480
}
481481
ParamLoweringMode::Variable => {
482-
let idx = generics.param_idx(param_id.into()).expect("matching generics");
482+
let idx = match generics.param_idx(param_id.into()) {
483+
None => {
484+
never!("no matching generics");
485+
return (TyKind::Error.intern(Interner), None);
486+
}
487+
Some(idx) => idx,
488+
};
489+
483490
TyKind::BoundVar(BoundVar::new(self.in_binders, idx))
484491
}
485492
}

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

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

1529+
#[test]
1530+
fn gat_crash_3() {
1531+
// FIXME: This test currently crashes rust analyzer in a debug build but not in a
1532+
// release build (i.e. for the user). With the assumption that tests will always be run
1533+
// in debug mode, we catch the unwind and expect that it panicked. See the
1534+
// [`crate::utils::generics`] function for more information.
1535+
cov_mark::check!(ignore_gats);
1536+
std::panic::catch_unwind(|| {
1537+
check_no_mismatches(
1538+
r#"
1539+
trait Collection {
1540+
type Item;
1541+
type Member<T>: Collection<Item = T>;
1542+
fn add(&mut self, value: Self::Item) -> Result<(), Self::Error>;
1543+
}
1544+
struct ConstGen<T, const N: usize> {
1545+
data: [T; N],
1546+
}
1547+
impl<T, const N: usize> Collection for ConstGen<T, N> {
1548+
type Item = T;
1549+
type Member<U> = ConstGen<U, N>;
1550+
}
1551+
"#,
1552+
);
1553+
})
1554+
.expect_err("must panic");
1555+
}
1556+
15291557
#[test]
15301558
fn cfgd_out_self_param() {
15311559
cov_mark::check!(cfgd_out_self_param);

crates/hir-ty/src/utils.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,10 +176,19 @@ 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)
187+
//
188+
// Note: Also crashes when the parent has const generics (also even if the GAT
189+
// doesn't use them), see `tests::regression::gat_crash_3` for an example.
190+
// Avoids that by disabling GATs when the parent (i.e. `impl` block) has
191+
// const generics (#12193).
183192
//
184193
// Chalk expects the inner associated type's parameters to come
185194
// *before*, not after the trait's generics as we've always done it.
@@ -264,12 +273,8 @@ impl Generics {
264273

265274
fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
266275
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();
276+
let (idx, (_local_id, data)) =
277+
self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
273278
let parent_len = self.parent_generics().map_or(0, Generics::len);
274279
Some((parent_len + idx, data))
275280
} else {

0 commit comments

Comments
 (0)