Repository: rust-lang/rust-analyzer (rust)

Issue:
Stack overflow when using type_alias_impl_trait with async functions
**rust-analyzer version**: rust-analyzer 0.0.0 (72dfbe95d 2024-06-10)

**rustc version**: rustc 1.80.0-nightly (1871252fc 2024-05-15)

**editor or extension**: Zed

**code snippet to reproduce**: 
```rust
#![feature(type_alias_impl_trait)]

type TypeAlias = impl Clone;

async fn alias() -> TypeAlias {
    todo!()
}
```

This causes an infinite recursion in `insert_inference_vars_for_impl_trait`, eventually resulting in a stack overflow which crashes RA.

My very naive fix for this issue was to add a set of already seen types to avoid `fold_tys` recursing, but this causes other issues down the road:

```
stderr: 2024-06-12T08:06:33.796309Z ERROR hir_ty::infer: Missed RPIT in `insert_inference_vars_for_rpit`
stderr: Panic context:
stderr: > fetch_native_diagnostics
stderr:
stderr: thread 'Worker' panicked at crates/hir/src/semantics.rs:1516:13:
stderr:
stderr:
stderr: Failed to lookup BLOCK_EXPR@96..111 in this Semantics.
stderr: Make sure to use only query nodes, derived from this instance of Semantics.
stderr: root node:   SOURCE_FILE@0..112
stderr: known nodes: SOURCE_FILE@0..112
stderr:
stderr:
stderr: stack backtrace:
stderr: 0: _rust_begin_unwind
stderr: 1: core::panicking::panic_fmt
stderr: 2: hir::semantics::SemanticsImpl::find_file
stderr: 3: hir::semantics::SemanticsImpl::analyze_impl
stderr: 4: hir::semantics::SemanticsImpl::scope
stderr: 5: ide_diagnostics::handlers::type_mismatch::type_mismatch
stderr: 6: ide_diagnostics::diagnostics
stderr: 7: salsa::Cancelled::catch
stderr: 8: ide::Analysis::diagnostics
stderr: 9: core::ops::function::impls::<impl core::ops::function::FnMut<A> for &mut F>::call_mut
stderr: 10: <alloc::vec::Vec<T> as alloc::vec::spec_from_iter::SpecFromIter<T,I>>::from_iter
stderr: 11: rust_analyzer::diagnostics::fetch_native_diagnostics
stderr: 12: core::ops::function::FnOnce::call_once{{vtable.shim}}
stderr: note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
```


Pull request:
Use proper `ImplTraits` in `insert_inference_vars_for_impl_trait`
Fixes #17199 and fixes #17403

In the previous implementation, I passed `rpits` as a function parameter and used `idx` of `ImplTraitId` for indexing `ImplTrait`.

https://github.com/rust-lang/rust-analyzer/blob/4e836c622a7bdab41be8e82733dd9fe40af128b2/crates/hir-ty/src/infer.rs#L881-L887

But that `idx` is rather a "local" one, so in the cases like mentioned issues, the async function that can be expanded roughly as

```rust
type TypeAlias = impl Something;
fn expanded_async() -> impl Future<Output = TypeAlias> { ... }
```

there are two bundles of `ImplTraits`; one for the `impl Future` and the other one for `TypeAlias`.
So using `idx` with `rpits` returns `ImplTrait` for `impl Future` even if we are asking for `TypeAlias` and this caused a stack overflow.

This PR is a fix for that implementation miss 😅 


Patch:
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs
index 96431ba4ce..f3ad4e6869 100644
--- a/crates/hir-ty/src/infer.rs
+++ b/crates/hir-ty/src/infer.rs
@@ -835,11 +835,7 @@ impl<'a> InferenceContext<'a> {
         let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
             // RPIT opaque types use substitution of their parent function.
             let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
-            let result = self.insert_inference_vars_for_impl_trait(
-                return_ty,
-                rpits.clone(),
-                fn_placeholders,
-            );
+            let result = self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders);
             let rpits = rpits.skip_binders();
             for (id, _) in rpits.impl_traits.iter() {
                 if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@@ -862,12 +858,7 @@ impl<'a> InferenceContext<'a> {
         self.insert_atpit_coercion_table(params_and_ret_tys.iter());
     }
 
-    fn insert_inference_vars_for_impl_trait<T>(
-        &mut self,
-        t: T,
-        rpits: Arc<chalk_ir::Binders<crate::ImplTraits>>,
-        placeholders: Substitution,
-    ) -> T
+    fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
     where
         T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
     {
@@ -878,13 +869,21 @@ impl<'a> InferenceContext<'a> {
                     TyKind::OpaqueType(opaque_ty_id, _) => *opaque_ty_id,
                     _ => return ty,
                 };
-                let idx = match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
-                    ImplTraitId::ReturnTypeImplTrait(_, idx) => idx,
-                    ImplTraitId::AssociatedTypeImplTrait(_, idx) => idx,
-                    _ => unreachable!(),
+                let (impl_traits, idx) =
+                    match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
+                        ImplTraitId::ReturnTypeImplTrait(def, idx) => {
+                            (self.db.return_type_impl_traits(def), idx)
+                        }
+                        ImplTraitId::AssociatedTypeImplTrait(def, idx) => {
+                            (self.db.type_alias_impl_traits(def), idx)
+                        }
+                        _ => unreachable!(),
+                    };
+                let Some(impl_traits) = impl_traits else {
+                    return ty;
                 };
-                let bounds =
-                    (*rpits).map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
+                let bounds = (*impl_traits)
+                    .map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
                 let var = self.table.new_type_var();
                 let var_subst = Substitution::from1(Interner, var.clone());
                 for bound in bounds {
@@ -892,11 +891,8 @@ impl<'a> InferenceContext<'a> {
                     let (var_predicate, binders) =
                         predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
                     always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
-                    let var_predicate = self.insert_inference_vars_for_impl_trait(
-                        var_predicate,
-                        rpits.clone(),
-                        placeholders.clone(),
-                    );
+                    let var_predicate = self
+                        .insert_inference_vars_for_impl_trait(var_predicate, placeholders.clone());
                     self.push_obligation(var_predicate.cast(Interner));
                 }
                 self.result.type_of_rpit.insert(idx, var.clone());
@@ -983,16 +979,8 @@ impl<'a> InferenceContext<'a> {
                     self.db.lookup_intern_impl_trait_id(opaque_ty_id.into())
                 {
                     if assoc_tys.contains(&alias_id) {
-                        let atpits = self
-                            .db
-                            .type_alias_impl_traits(alias_id)
-                            .expect("Marked as ATPIT but no impl traits!");
                         let alias_placeholders = TyBuilder::placeholder_subst(self.db, alias_id);
-                        let ty = self.insert_inference_vars_for_impl_trait(
-                            ty,
-                            atpits,
-                            alias_placeholders,
-                        );
+                        let ty = self.insert_inference_vars_for_impl_trait(ty, alias_placeholders);
                         return Some((opaque_ty_id, ty));
                     }
                 }


Test patch:
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 3aa94be755..4fcb6062b6 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -1999,3 +1999,22 @@ where
 "#,
     );
 }
+
+#[test]
+fn tait_async_stack_overflow_17199() {
+    check_types(
+        r#"
+    //- minicore: fmt, future
+    type Foo = impl core::fmt::Debug;
+
+    async fn foo() -> Foo {
+        ()
+    }
+
+    async fn test() {
+        let t = foo().await;
+         // ^ impl Debug
+    }
+"#,
+    );
+}