Skip to content

check coroutines with TypingMode::Borrowck to avoid cyclic reasoning #141125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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(())
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/coroutine/delayed-obligations-emit.next.stderr
Original file line number Diff line number Diff line change
@@ -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`.
33 changes: 33 additions & 0 deletions tests/ui/coroutine/delayed-obligations-emit.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading