Rollup merge of #141125 - lcnr:coroutine_obligations_use_borrowck, r=compiler-errors

check coroutines with `TypingMode::Borrowck` to avoid cyclic reasoning

MIR borrowck taints its output if an obligation fails. This could then cause `check_coroutine_obligations` to silence its error, causing us to not emit and actual error and ICE.

Fixes the ICE in https://github.com/rust-lang/trait-system-refactor-initiative/issues/199. It is unfortunately still a regression.

r? compiler-errors
This commit is contained in:
Matthias Krüger 2025-05-17 15:45:22 +02:00 committed by GitHub
commit 909098728c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 61 additions and 7 deletions

View file

@ -1754,17 +1754,19 @@ pub(super) fn check_coroutine_obligations(
debug!(?typeck_results.coroutine_stalled_predicates);
let mode = if tcx.next_trait_solver_globally() {
TypingMode::post_borrowck_analysis(tcx, def_id)
// This query is conceptually between HIR typeck and
// MIR borrowck. We use the opaque types defined by HIR
// and ignore region constraints.
TypingMode::borrowck(tcx, def_id)
} else {
TypingMode::analysis_in_body(tcx, def_id)
};
let infcx = tcx
.infer_ctxt()
// typeck writeback gives us predicates with their regions erased.
// As borrowck already has checked lifetimes, we do not need to do it again.
.ignoring_regions()
.build(mode);
// Typeck writeback gives us predicates with their regions erased.
// We only need to check the goals while ignoring lifetimes to give good
// error message and to avoid breaking the assumption of `mir_borrowck`
// that all obligations already hold modulo regions.
let infcx = tcx.infer_ctxt().ignoring_regions().build(mode);
let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
for (predicate, cause) in &typeck_results.coroutine_stalled_predicates {
@ -1785,6 +1787,10 @@ pub(super) fn check_coroutine_obligations(
let key = infcx.resolve_vars_if_possible(key);
sanity_check_found_hidden_type(tcx, key, hidden_type)?;
}
} else {
// We're not checking region constraints here, so we can simply drop the
// added opaque type uses in `TypingMode::Borrowck`.
let _ = infcx.take_opaque_types();
}
Ok(())

View file

@ -0,0 +1,15 @@
error[E0275]: overflow evaluating the requirement `{async block@$DIR/delayed-obligations-emit.rs:17:11: 17:16}: Send`
--> $DIR/delayed-obligations-emit.rs:17:5
|
LL | spawn(async { build_dependencies().await });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: required by a bound in `spawn`
--> $DIR/delayed-obligations-emit.rs:31:13
|
LL | fn spawn<F: Send>(_: F) {}
| ^^^^ required by this bound in `spawn`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0275`.

View file

@ -0,0 +1,33 @@
//@ revisions: current next
//@ ignore-compare-mode-next-solver (explicit revisions)
//@[next] compile-flags: -Znext-solver
//@ edition: 2024
//@[current] check-pass
// This previously caused an ICE with the new solver.
// The delayed coroutine obligations were checked with the
// opaque types inferred by borrowck.
//
// One of these delayed obligations failed with overflow in
// borrowck, causing us to taint `type_of` for the opaque. This
// then caused us to also not emit an error when checking the
// coroutine obligations.
fn build_multiple<'a>() -> impl Sized {
spawn(async { build_dependencies().await });
//[next]~^ ERROR overflow evaluating the requirement
}
// Adding an explicit `Send` bound fixes it.
// Proving `build_dependencies(): Send` in `build_multiple` adds
// addiitional defining uses/placeholders.
fn build_dependencies() -> impl Future<Output = ()> /* + Send */ {
async {
Box::pin(build_dependencies()).await;
async { build_multiple() }.await;
}
}
fn spawn<F: Send>(_: F) {}
fn main() {}