1
Fork 0

Rollup merge of #118161 - compiler-errors:coroutine-obligation-opaques, r=lcnr

Allow defining opaques in `check_coroutine_obligations`

In the new trait solver, when an obligation stalls on an unresolved coroutine witness, we will stash away the *root* obligation, even if the stalled obligation is only a distant descendent of the root obligation, since the new solver is purely recursive.

This means that we may need to reprocess alias-relate obligations (and others) which may define opaque types in the new solver. Currently, we use the coroutine's def id as the defining anchor in `check_coroutine_obligations`, which will allow defining no opaque types, resulting in errors like:

```
error[E0271]: type mismatch resolving `{coroutine@<source>:6:5: 6:17} <: impl Clone`
 --> <source>:6:5
  |
6 | /     move |_: ()| {
7 | |         let () = yield ();
8 | |     }
  | |_____^ types differ
```

So this PR fixes the defining anchor and does the same trick as `check_opaque_well_formed`, where we manually compare opaques that were defined against their hidden types to make sure they weren't defined differently when processing these stalled coroutine obligations.

r? `@lcnr` cc `@cjgillot`
This commit is contained in:
Michael Goulet 2023-11-22 09:28:52 -08:00 committed by GitHub
commit 4ec548afbe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 5 deletions

View file

@ -1468,7 +1468,10 @@ fn opaque_type_cycle_error(
err.emit() err.emit()
} }
pub(super) fn check_coroutine_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) { pub(super) fn check_coroutine_obligations(
tcx: TyCtxt<'_>,
def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Coroutine)); debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Coroutine));
let typeck = tcx.typeck(def_id); let typeck = tcx.typeck(def_id);
@ -1482,8 +1485,9 @@ pub(super) fn check_coroutine_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
// typeck writeback gives us predicates with their regions erased. // typeck writeback gives us predicates with their regions erased.
// As borrowck already has checked lifetimes, we do not need to do it again. // As borrowck already has checked lifetimes, we do not need to do it again.
.ignoring_regions() .ignoring_regions()
// Bind opaque types to `def_id` as they should have been checked by borrowck. // Bind opaque types to type checking root, as they should have been checked by borrowck,
.with_opaque_type_inference(DefiningAnchor::Bind(def_id)) // but may show up in some cases, like when (root) obligations are stalled in the new solver.
.with_opaque_type_inference(DefiningAnchor::Bind(typeck.hir_owner.def_id))
.build(); .build();
let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx); let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx);
@ -1513,6 +1517,16 @@ pub(super) fn check_coroutine_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let errors = fulfillment_cx.select_all_or_error(&infcx); let errors = fulfillment_cx.select_all_or_error(&infcx);
debug!(?errors); debug!(?errors);
if !errors.is_empty() { if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors); return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
} }
// Check that any hidden types found when checking these stalled coroutine obligations
// are valid.
for (key, ty) in infcx.take_opaque_types() {
let hidden_type = infcx.resolve_vars_if_possible(ty.hidden_type);
let key = infcx.resolve_vars_if_possible(key);
sanity_check_found_hidden_type(tcx, key, hidden_type)?;
}
Ok(())
} }

View file

@ -567,7 +567,7 @@ rustc_queries! {
separate_provide_extern separate_provide_extern
} }
query check_coroutine_obligations(key: LocalDefId) { query check_coroutine_obligations(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) } desc { |tcx| "verify auto trait bounds for coroutine interior type `{}`", tcx.def_path_str(key) }
} }

View file

@ -0,0 +1,17 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
// check-pass
#![feature(coroutines, coroutine_trait, coroutine_clone)]
// This stalls the goal `{coroutine} <: impl Clone`, since that has a nested goal
// of `{coroutine}: Clone`. That is only known if we can compute the generator
// witness types, which we don't know until after borrowck. When we later check
// the goal for correctness, we want to be able to bind the `impl Clone` opaque.
pub fn foo<'a, 'b>() -> impl Clone {
move |_: ()| {
let () = yield ();
}
}
fn main() {}