about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/lower.rs9
-rw-r--r--crates/hir-ty/src/tests/regression.rs28
-rw-r--r--crates/hir-ty/src/utils.rs21
3 files changed, 49 insertions, 9 deletions
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index ae115c8c0da..8b360337a82 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -479,7 +479,14 @@ impl<'a> TyLoweringContext<'a> {
                         TyKind::Placeholder(to_placeholder_idx(self.db, param_id.into()))
                     }
                     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))
                     }
                 }
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 1b5ed0603bf..c7895db1afb 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -1527,6 +1527,34 @@ unsafe impl Storage for InlineStorage {
 }
 
 #[test]
+fn gat_crash_3() {
+    // FIXME: This test currently crashes rust analyzer in a debug build but not in a
+    // release build (i.e. for the user). With the assumption that tests will always be run
+    // in debug mode, we catch the unwind and expect that it panicked. See the
+    // [`crate::utils::generics`] function for more information.
+    cov_mark::check!(ignore_gats);
+    std::panic::catch_unwind(|| {
+        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>;
+}
+        "#,
+        );
+    })
+    .expect_err("must panic");
+}
+
+#[test]
 fn cfgd_out_self_param() {
     cov_mark::check!(cfgd_out_self_param);
     check_no_mismatches(
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 83319755da7..d6638db0285 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -176,10 +176,19 @@ 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)));
     if parent_generics.is_some() && matches!(def, GenericDefId::TypeAliasId(_)) {
         let params = db.generic_params(def);
+        let parent_params = &parent_generics.as_ref().unwrap().params;
         let has_consts =
             params.iter().any(|(_, x)| matches!(x, TypeOrConstParamData::ConstParamData(_)));
-        return if has_consts {
-            // XXX: treat const generic associated types as not existing to avoid crashes (#11769)
+        let parent_has_consts =
+            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)
+            //
+            // 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.
+            // Avoids that by disabling GATs when the parent (i.e. `impl` block) has
+            // const generics (#12193).
             //
             // Chalk expects the inner associated type's parameters to come
             // *before*, not after the trait's generics as we've always done it.
@@ -264,12 +273,8 @@ impl Generics {
 
     fn find_param(&self, param: TypeOrConstParamId) -> Option<(usize, &TypeOrConstParamData)> {
         if param.parent == self.def {
-            let (idx, (_local_id, data)) = self
-                .params
-                .iter()
-                .enumerate()
-                .find(|(_, (idx, _))| *idx == param.local_id)
-                .unwrap();
+            let (idx, (_local_id, data)) =
+                self.params.iter().enumerate().find(|(_, (idx, _))| *idx == param.local_id)?;
             let parent_len = self.parent_generics().map_or(0, Generics::len);
             Some((parent_len + idx, data))
         } else {