rust-lang / rust-lang/rust-clippy

ICE: Unstable fingerprints for `evaluate_obligation` during incremental clippy with async closures + blanket-impl trait

Open
#16,729 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-bug I-ICE
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

Summary

cargo clippy panics with "Found unstable fingerprints for evaluate_obligation" on every incremental rebuild when an async function chains .filter_map(async |...|) (via a blanket-impl indirection trait resembling futures_util::fns::FnMut1) and the future_not_send lint (nursery group) is enabled.

The ICE reproduces deterministically: first cargo clippy succeeds, then any subsequent incremental run (touch src/main.rs && cargo clippy) crashes.

Minimal reproduction

Three files, one crate (lib + bin targets):

Cargo.toml

[package]
name = "ice-repro"
version = "0.1.0"
edition = "2024"

[profile.dev]
incremental = true

[lints.clippy]
nursery = { level = "warn", priority = -1 }

src/lib.rs

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};

pub trait FnMut1<A> {
    type Output;
    fn call_mut(&mut self, arg: A) -> Self::Output;
}
impl<T, A, R> FnMut1<A> for T
where
    T: FnMut(A) -> R,
{
    type Output = R;
    fn call_mut(&mut self, arg: A) -> R {
        self(arg)
    }
}

pub trait Stream {
    type Item;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
}

pub struct Iter<I>(pub I);
impl<I: Iterator> Stream for Iter<I> {
    type Item = I::Item;
    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Poll::Pending
    }
}

pub struct FilterMap<S, F>(pub S, pub F);
impl<S, F, T> Stream for FilterMap<S, F>
where
    S: Stream,
    F: FnMut1<S::Item, Output: Future<Output = Option<T>>>,
{
    type Item = T;
    fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        Poll::Pending
    }
}

pub struct Fold<S, F, A>(pub S, pub F, pub Option<A>);
impl<S, F, Fut, A> Future for Fold<S, F, A>
where
    S: Stream,
    F: FnMut(A, S::Item) -> Fut,
    Fut: Future<Output = A>,
{
    type Output = A;
    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<A> {
        Poll::Pending
    }
}

pub trait StreamExt: Stream + Sized {
    fn filter_map<F, Fut, T>(self, f: F) -> FilterMap<Self, F>
    where
        F: FnMut1<Self::Item, Output = Fut>,
        Fut: Future<Output = Option<T>>,
    {
        FilterMap(self, f)
    }
    fn fold<F, Fut, A>(self, acc: A, f: F) -> Fold<Self, F, A>
    where
        F: FnMut(A, Self::Item) -> Fut,
        Fut: Future<Output = A>,
    {
        Fold(self, f, Some(acc))
    }
}
impl<S: Stream> StreamExt for S {}

pub fn iter<I: IntoIterator>(i: I) -> Iter<I::IntoIter> {
    Iter(i.into_iter())
}

src/main.rs

#![allow(unused)]

use ice_repro::{StreamExt, iter};

async fn find_latest(dirs: &[String]) -> Option<String> {
    iter(dirs.iter())
        .filter_map(async |d: &String| -> Option<String> { Some(d.clone()) })
        .filter_map(async |d: String| -> Option<String> { Some(d) })
        .fold(None, async |_: Option<String>, b: String| -> Option<String> { Some(b) })
        .await
}

fn main() {}

Steps to reproduce
cargo clippy            # first pass: populates incremental cache
touch src/main.rs       # invalidate the binary target
cargo clippy            # second pass: ICE
Notes

All four of the following appear to be required to trigger the crash:

  1. incremental = true
  2. clippy's future_not_send lint enabled (via nursery = "warn")
  3. a blanket impl indirection trait in a separate compilation unit
  4. Two chained .filter_map(async |...|) calls + .fold(...).await

In practice this affects any project that uses futures::StreamExt::filter_map with async closures and has nursery lints enabled. futures_util::fns::FnMut1 is the real-world equivalent of the FnMut1 trait in the repro. The underlying bug appears to be in rustc's incremental compilation fingerprint verification (rustc_middle::verify_ich), not in clippy itself -- clippy's future_not_send lint merely exercises the affected code path.

Version
rustc 1.96.0-nightly (b41f22de2 2026-03-08)
binary: rustc
commit-hash: b41f22de2a13a0babd28771e96feef4c309f54aa
commit-date: 2026-03-08
host: x86_64-unknown-linux-gnu
release: 1.96.0-nightly
LLVM version: 22.1.0


Tested on current stable (0.1.94 4a4ef493e3 2026-03-02) as well.
Error output
Backtrace

thread 'rustc' panicked at /rustc-dev/b41f22de2a13a0babd28771e96feef4c309f54aa/compiler/rustc_middle/src/verify_ich.rs:80:9:
Found unstable fingerprints for evaluate_obligation(49b9c257e6e345e6-85c3452aa79dbe26): Ok(EvaluatedToOk)
stack backtrace:
   0:     0x14f15cbef72b - <std[f377252fd1978e75]::backtrace::Backtrace>::create
   1:     0x14f15cbef675 - <std[f377252fd1978e75]::backtrace::Backtrace>::force_capture
   2:     0x14f15bbf3a6b - std[f377252fd1978e75]::panicking::update_hook::<alloc[7d80c8e5f2bc07aa]::boxed::Box<rustc_driver_impl[a7e284e8ff2767b6]::install_ice_hook::{closure#1}>>::{closure#0}
   3:     0x14f15cc01ca2 - std[f377252fd1978e75]::panicking::panic_with_hook
   4:     0x14f15cbe46f8 - std[f377252fd1978e75]::panicking::panic_handler::{closure#0}
   5:     0x14f15cbdb429 - std[f377252fd1978e75]::sys::backtrace::__rust_end_short_backtrace::<std[f377252fd1978e75]::panicking::panic_handler::{closure#0}, !>
   6:     0x14f15cbe615d - __rustc[d65e30e194c7d9cd]::rust_begin_unwind
   7:     0x14f1597aa0cc - core[40ef8ddb62afa269]::panicking::panic_fmt
   8:     0x14f15c22e485 - rustc_middle[6d7cfdcb6c95edb7]::verify_ich::incremental_verify_ich_failed
   9:     0x14f15db8bbff - rustc_middle[6d7cfdcb6c95edb7]::verify_ich::incremental_verify_ich::<rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 2usize]>>
  10:     0x14f15da0503c - rustc_query_impl[5ff32ada1adb4c7a]::execution::try_execute_query::<rustc_middle[6d7cfdcb6c95edb7]::query::caches::DefaultCache<rustc_type_ir[cecd37ed784ac10d]::canonical::CanonicalQueryInput<rustc_middle[6d7cfdcb6c95edb7]::ty::context::TyCtxt, rustc_middle[6d7cfdcb6c95edb7]::ty::ParamEnvAnd<rustc_middle[6d7cfdcb6c95edb7]::ty::predicate::Predicate>>, rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 2usize]>>, true>
  11:     0x14f15da02fc2 - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::evaluate_obligation::execute_query_incr::__rust_end_short_backtrace
  12:     0x14f15dac756b - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::FulfillProcessor as rustc_data_structures[86b662b2b592701f]::obligation_forest::ObligationProcessor>::process_obligation
  13:     0x14f15d2b8dc1 - <rustc_data_structures[86b662b2b592701f]::obligation_forest::ObligationForest<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::PendingPredicateObligation>>::process_obligations::<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::FulfillProcessor>
  14:     0x14f15dc454b9 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::engine::ObligationCtxt>::make_canonicalized_query_response::<()>
  15:     0x14f15dc3f460 - rustc_traits[ab8d48508fb7851e]::type_op::type_op_prove_predicate
  16:     0x14f15db37576 - rustc_query_impl[5ff32ada1adb4c7a]::execution::try_execute_query::<rustc_middle[6d7cfdcb6c95edb7]::query::caches::DefaultCache<rustc_type_ir[cecd37ed784ac10d]::canonical::CanonicalQueryInput<rustc_middle[6d7cfdcb6c95edb7]::ty::context::TyCtxt, rustc_middle[6d7cfdcb6c95edb7]::ty::ParamEnvAnd<rustc_middle[6d7cfdcb6c95edb7]::traits::query::type_op::ProvePredicate>>, rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 8usize]>>, true>
  17:     0x14f15db3616b - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::type_op_prove_predicate::execute_query_incr::__rust_end_short_backtrace
  18:     0x14f15d9e6a9d - <rustc_borrowck[2d9d68db0deb061]::type_check::TypeChecker>::normalize_and_prove_instantiated_predicates
  19:     0x14f15a84af56 - <rustc_borrowck[2d9d68db0deb061]::type_check::TypeChecker as rustc_middle[6d7cfdcb6c95edb7]::mir::visit::Visitor>::visit_operand
  20:     0x14f15e21885d - <rustc_borrowck[2d9d68db0deb061]::type_check::TypeChecker as rustc_middle[6d7cfdcb6c95edb7]::mir::visit::Visitor>::visit_body
  21:     0x14f15e201f62 - rustc_borrowck[2d9d68db0deb061]::type_check::type_check
  22:     0x14f15e3f3314 - <rustc_borrowck[2d9d68db0deb061]::root_cx::BorrowCheckRootCtxt>::do_mir_borrowck
  23:     0x14f15e3eec6f - rustc_borrowck[2d9d68db0deb061]::mir_borrowck
  24:     0x14f15e3eeab5 - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::mir_borrowck::invoke_provider_fn::__rust_begin_short_backtrace
  25:     0x14f15d455c57 - rustc_query_impl[5ff32ada1adb4c7a]::execution::try_execute_query::<rustc_data_structures[86b662b2b592701f]::vec_cache::VecCache<rustc_span[825691e18cdd0684]::def_id::LocalDefId, rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 8usize]>, rustc_middle[6d7cfdcb6c95edb7]::dep_graph::graph::DepNodeIndex>, true>
  26:     0x14f15d44dc0e - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::mir_borrowck::execute_query_incr::__rust_end_short_backtrace
  27:     0x14f15e27d68e - rustc_hir_analysis[c936938903594311]::collect::type_of::opaque::find_opaque_ty_constraints_for_rpit
  28:     0x14f15e27d366 - rustc_hir_analysis[c936938903594311]::collect::type_of::type_of_opaque
  29:     0x14f15d45dab2 - rustc_query_impl[5ff32ada1adb4c7a]::execution::try_execute_query::<rustc_middle[6d7cfdcb6c95edb7]::query::caches::DefIdCache<rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 8usize]>>, true>
  30:     0x14f15e81071c - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::type_of_opaque::execute_query_incr::__rust_end_short_backtrace
  31:     0x14f15d6dbbf3 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::select::SelectionContext>::confirm_auto_impl_candidate::{closure#0}
  32:     0x14f15d37a603 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::select::SelectionContext>::poly_select::{closure#0}
  33:     0x14f15dac3bb9 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::FulfillProcessor as rustc_data_structures[86b662b2b592701f]::obligation_forest::ObligationProcessor>::process_obligation
  34:     0x14f15d2b8dc1 - <rustc_data_structures[86b662b2b592701f]::obligation_forest::ObligationForest<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::PendingPredicateObligation>>::process_obligations::<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::FulfillProcessor>
  35:     0x14f15dc19a30 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::fulfill::FulfillmentContext<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::FulfillmentError> as rustc_infer[f695937d0203f373]::traits::engine::TraitEngine<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::FulfillmentError>>::evaluate_obligations_error_on_ambiguity
  36:     0x55dc5fa4bcd1 - <rustc_trait_selection[1844f0ca5ec2ecf5]::traits::engine::ObligationCtxt<rustc_trait_selection[1844f0ca5ec2ecf5]::traits::FulfillmentError>>::evaluate_obligations_error_on_ambiguity
  37:     0x55dc5fb59d46 - <clippy_lints[42090889051f29c9]::future_not_send::FutureNotSend as rustc_lint[52e19671ac4a625f]::passes::LateLintPass>::check_fn
  38:     0x14f15c15cdd8 - <rustc_lint[52e19671ac4a625f]::late::LateContextAndPass<rustc_lint[52e19671ac4a625f]::late::RuntimeCombinedLateLintPass> as rustc_hir[88d14f16e404e101]::intravisit::Visitor>::visit_fn
  39:     0x14f15c170e4c - <rustc_lint[52e19671ac4a625f]::late::LateContextAndPass<rustc_lint[52e19671ac4a625f]::late::RuntimeCombinedLateLintPass> as rustc_hir[88d14f16e404e101]::intravisit::Visitor>::visit_nested_item
  40:     0x14f15c0fbeb9 - <rustc_lint[52e19671ac4a625f]::late::LateContextAndPass<rustc_lint[52e19671ac4a625f]::late::RuntimeCombinedLateLintPass>>::process_mod
  41:     0x14f15e0b3177 - rustc_lint[52e19671ac4a625f]::late::check_crate::{closure#0}
  42:     0x14f15e0b247f - rustc_lint[52e19671ac4a625f]::late::check_crate
  43:     0x14f15e0af83e - rustc_interface[5f8c59f625471e6f]::passes::analysis::{closure#0}::{closure#0}::{closure#2}
  44:     0x14f15e0af61f - rustc_data_structures[86b662b2b592701f]::sync::parallel::par_fns
  45:     0x14f15e0af5bc - rustc_interface[5f8c59f625471e6f]::passes::analysis::{closure#0}::{closure#0}
  46:     0x14f15e0af61f - rustc_data_structures[86b662b2b592701f]::sync::parallel::par_fns
  47:     0x14f15d44fdf1 - rustc_interface[5f8c59f625471e6f]::passes::analysis
  48:     0x14f15e88798f - rustc_query_impl[5ff32ada1adb4c7a]::execution::try_execute_query::<rustc_middle[6d7cfdcb6c95edb7]::query::caches::SingleCache<rustc_middle[6d7cfdcb6c95edb7]::query::erase::ErasedData<[u8; 0usize]>>, true>
  49:     0x14f15e8872de - rustc_query_impl[5ff32ada1adb4c7a]::query_impl::analysis::execute_query_incr::__rust_end_short_backtrace
  50:     0x14f15e4eb788 - rustc_interface[5f8c59f625471e6f]::interface::run_compiler::<(), rustc_driver_impl[a7e284e8ff2767b6]::run_compiler::{closure#0}>::{closure#1}
  51:     0x14f15e51ea7e - std[f377252fd1978e75]::sys::backtrace::__rust_begin_short_backtrace::<rustc_interface[5f8c59f625471e6f]::util::run_in_thread_with_globals<rustc_interface[5f8c59f625471e6f]::util::run_in_thread_pool_with_globals<rustc_interface[5f8c59f625471e6f]::interface::run_compiler<(), rustc_driver_impl[a7e284e8ff2767b6]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>
  52:     0x14f15e51f320 - <std[f377252fd1978e75]::thread::lifecycle::spawn_unchecked<rustc_interface[5f8c59f625471e6f]::util::run_in_thread_with_globals<rustc_interface[5f8c59f625471e6f]::util::run_in_thread_pool_with_globals<rustc_interface[5f8c59f625471e6f]::interface::run_compiler<(), rustc_driver_impl[a7e284e8ff2767b6]::run_compiler::{closure#0}>::{closure#1}, ()>::{closure#0}, ()>::{closure#0}::{closure#0}, ()>::{closure#1} as core[40ef8ddb62afa269]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}
  53:     0x14f15e5201ac - <std[f377252fd1978e75]::sys::thread::unix::Thread>::new::thread_start
  54:     0x14f1578621cf - start_thread
                               at /usr/src/debug/glibc-2.28-211.el8.x86_64/nptl/pthread_create.c:479:8
  55:     0x14f1574cde73 - clone
                               at /usr/src/debug/glibc-2.28-211.el8.x86_64/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:95:0
  56:                0x0 - <unknown>


rustc version: 1.96.0-nightly (b41f22de2 2026-03-08)
platform: x86_64-unknown-linux-gnu

query stack during panic:
#0 [evaluate_obligation] evaluating trait selection obligation `{async closure@src/main.rs:8:21: 8:56}: ice_repro::FnMut1<alloc::string::String>`
#1 [type_op_prove_predicate] evaluating `type_op_prove_predicate` `ProvePredicate { predicate: Binder { value: TraitPredicate(<{async closure@src/main.rs:8:21: 8:56} as ice_repro::FnMut1<alloc::string::String>>, polarity:Positive), bound_vars: [] } }`
#2 [mir_borrowck] borrow-checking `find_latest`
#3 [type_of_opaque] computing type of opaque `find_latest::{opaque#0}`
#4 [analysis] running analysis passes on crate `ice_repro`
end of query stack

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the three-file reproduction in Cargo.toml, src/lib.rs, and src/main.rs, then run cargo clippy, touch src/main.rs, and run it again. Read rustc_middle/src/verify_ich.rs and the future_not_send lint entry point shown in the backtrace. Done means the incremental second run completes without the unstable-fingerprint ICE for this reproduction.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.