rust-lang/rust · critical

coroutine lowered from async gen fn should be in fn

Error message

coroutine lowered from async gen fn should be in fn

What it means

This assertion fires inside rustc's borrow-checker region-naming diagnostics when generating a human-readable name for a region belonging to a coroutine that was desugared from an `async gen fn`. The code walks to the coroutine's parent HIR node and calls `.fn_decl().expect(...)` on it. It assumes an async-gen-fn coroutine is always nested inside a function item with an `fn_decl`. If the parent HIR node is not a function (has no fn_decl), the compiler panics with an ICE.

Source

Thrown at compiler/rustc_borrowck/src/diagnostics/region_name.rs:898

                    )) => " of async gen block",

                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
                        hir::CoroutineDesugaring::AsyncGen,
                        hir::CoroutineSource::Closure,
                    ))
                    | hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::AsyncGen) => {
                        " of async gen closure"
                    }

                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
                        hir::CoroutineDesugaring::AsyncGen,
                        hir::CoroutineSource::Fn,
                    )) => {
                        let parent_item =
                            tcx.hir_node_by_def_id(tcx.hir_get_parent_item(mir_hir_id).def_id);
                        let output = &parent_item
                            .fn_decl()
                            .expect("coroutine lowered from async gen fn should be in fn")
                            .output;
                        span = output.span();
                        " of async gen function"
                    }

                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_)) => {
                        " of coroutine"
                    }
                    hir::ClosureKind::Closure => " of closure",
                };
                (span, mir_description, hir_ty)
            }
            node => match node.fn_decl() {
                Some(fn_decl) => {
                    let hir_ty = match fn_decl.output {
                        hir::FnRetTy::DefaultReturn(_) => None,
                        hir::FnRetTy::Return(ty) => Some(ty),
                    };

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Check if the ICE reproduces on the latest nightly; async-gen support is rapidly evolving and this may already be fixed.
  2. Minimize the reproduction: remove async gen functions one by one until the panic disappears, then file a bug at https://github.com/rust-lang/rust/issues with the minimal case.
  3. Rewrite the `async gen fn` as a manual `impl Future` + `Gen` combination or a regular `async fn` returning a stream, to avoid the unstable desugaring path.
  4. If you are a compiler contributor, audit `region_name.rs` lines 890-902: the `parent_item` may need to handle non-`fn_decl` HIR nodes by falling back to `self.body.span` instead of panicking.

Example fix

// before (triggers ICE on some nightly versions)
async gen fn items() -> impl Stream<Item = u32> {
    yield 42;
}

// after (stable workaround using async-stream crate)
fn items() -> impl Stream<Item = u32> {
    async_stream::stream! { yield 42; }
}
Defensive patterns

Strategy: validation

Validate before calling

// Before relying on async gen fn, verify the compiler supports the
// specific HIR placement. As an end user you cannot directly validate
// compiler internals, but you can gate unstable features:
// In Cargo.toml or build.rs, check the nightly version:
#[cfg(not(feature = "stable-workaround"))]
compile_error!("If you hit ICE on async gen fn, enable feature stable-workaround");

Type guard

// No user-level type guard applies; this is a compiler-internal assertion
// on HIR structure. The 'guard' is avoiding async gen fn in unusual positions.
null

Try / catch

// Rust ICEs are panics; you can catch_unwind to report gracefully
// (compiler drivers only, not user crates):
std::panic::catch_unwind(|| {
    // invoke rustc/compilation here
}).unwrap_or_else(|_| {
    eprintln!("ICE encountered; falling back to default borrow checker");
});

Prevention

When it happens

Trigger: Compiling code that uses `async gen fn` (an unstable async generator function feature) where the desugared coroutine's parent HIR node does not carry an `fn_decl`. This can be triggered by unusual or not-yet-supported placements of async gen functions, or by bugs in HIR construction/coroutine desugaring for new syntax around async generators.

Common situations: Using nightly Rust with experimental `async gen fn` or coroutine-closure features in combinations the compiler hasn't fully accounted for. Often appears after a compiler upgrade that changes coroutine desugaring, or when async gen functions appear in unusual HIR positions (e.g. inside certain macro expansions or trait items).

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/4ef3c7c918a1dd76. Report an issue: GitHub.