Repository: rust-analyzer/rust-analyzer
Pull request #8440 (2021-04-09 09:17:27)

Fix crash on syn involving lifetimes returned by Chalk
If we get lifetime variables back in autoderef, just immediately replace them by static lifetimes for now. Method resolution doesn't really deal correctly with new variables being introduced (this needs to be fixed more properly).

This fixes `rust-analyzer analysis-stats --with-deps` crashing in the RA repo.


Patch:
diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs
index f8e9db9ae5..71bc436e6f 100644
--- a/crates/hir_ty/src/autoderef.rs
+++ b/crates/hir_ty/src/autoderef.rs
@@ -6,14 +6,15 @@
 use std::iter::successors;
 
 use base_db::CrateId;
-use chalk_ir::cast::Cast;
+use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind};
 use hir_def::lang_item::LangItemTarget;
 use hir_expand::name::name;
 use log::{info, warn};
 
 use crate::{
-    db::HirDatabase, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, DebruijnIndex,
-    InEnvironment, Interner, ProjectionTyExt, Solution, Ty, TyBuilder, TyKind,
+    db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds,
+    DebruijnIndex, InEnvironment, Interner, ProjectionTyExt, Solution, Substitution, Ty, TyBuilder,
+    TyKind,
 };
 
 const AUTODEREF_RECURSION_LIMIT: usize = 10;
@@ -103,7 +104,7 @@ fn deref_by_trait(
         binders: CanonicalVarKinds::from_iter(
             &Interner,
             ty.goal.binders.iter(&Interner).cloned().chain(Some(chalk_ir::WithKind::new(
-                chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
+                VariableKind::Ty(chalk_ir::TyVariableKind::General),
                 chalk_ir::UniverseIndex::ROOT,
             ))),
         ),
@@ -136,7 +137,9 @@ fn deref_by_trait(
                     return None;
                 }
             }
-            Some(Canonical {
+            // FIXME: we remove lifetime variables here since they can confuse
+            // the method resolution code later
+            Some(fixup_lifetime_variables(Canonical {
                 value: vars
                     .value
                     .subst
@@ -144,7 +147,7 @@ fn deref_by_trait(
                     .assert_ty_ref(&Interner)
                     .clone(),
                 binders: vars.binders.clone(),
-            })
+            }))
         }
         Solution::Ambig(_) => {
             info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution);
@@ -152,3 +155,32 @@ fn deref_by_trait(
         }
     }
 }
+
+fn fixup_lifetime_variables<T: Fold<Interner, Result = T> + HasInterner<Interner = Interner>>(
+    c: Canonical<T>,
+) -> Canonical<T> {
+    // Removes lifetime variables from the Canonical, replacing them by static lifetimes.
+    let mut i = 0;
+    let subst = Substitution::from_iter(
+        &Interner,
+        c.binders.iter(&Interner).map(|vk| match vk.kind {
+            VariableKind::Ty(_) => {
+                let index = i;
+                i += 1;
+                BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(&Interner).cast(&Interner)
+            }
+            VariableKind::Lifetime => static_lifetime().cast(&Interner),
+            VariableKind::Const(_) => unimplemented!(),
+        }),
+    );
+    let binders = CanonicalVarKinds::from_iter(
+        &Interner,
+        c.binders.iter(&Interner).filter(|vk| match vk.kind {
+            VariableKind::Ty(_) => true,
+            VariableKind::Lifetime => false,
+            VariableKind::Const(_) => true,
+        }),
+    );
+    let value = subst.apply(c.value, &Interner);
+    Canonical { binders, value }
+}
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index be3e4f09ab..1b60cb7279 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -609,6 +609,7 @@ fn iterate_trait_method_candidates(
                 }
             }
             known_implemented = true;
+            // FIXME: we shouldn't be ignoring the binders here
             if callback(&self_ty.value, *item) {
                 return true;
             }


Test patch:
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index b69f860502..9cd9f473d7 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -974,3 +974,41 @@ fn param_overrides_fn() {
         "#,
     )
 }
+
+#[test]
+fn lifetime_from_chalk_during_deref() {
+    check_types(
+        r#"
+        #[lang = "deref"]
+        pub trait Deref {
+            type Target;
+        }
+
+        struct Box<T: ?Sized> {}
+        impl<T> Deref for Box<T> {
+            type Target = T;
+
+            fn deref(&self) -> &Self::Target {
+                loop {}
+            }
+        }
+
+        trait Iterator {
+            type Item;
+        }
+
+        pub struct Iter<'a, T: 'a> {
+            inner: Box<dyn IterTrait<'a, T, Item = &'a T> + 'a>,
+        }
+
+        trait IterTrait<'a, T: 'a>: Iterator<Item = &'a T> {
+            fn clone_box(&self);
+        }
+
+        fn clone_iter<T>(s: Iter<T>) {
+            s.inner.clone_box();
+          //^^^^^^^^^^^^^^^^^^^ ()
+        }
+        "#,
+    )
+}