fudge infer vars in cause code intentionally

This commit is contained in:
Adwin White 2025-11-24 14:45:52 +08:00
parent fb505a7985
commit b111aed11f
6 changed files with 152 additions and 0 deletions

View file

@ -37,6 +37,8 @@ use crate::infer::InferCtxt;
#[derive(Clone, TypeFoldable, TypeVisitable)]
pub struct Obligation<'tcx, T> {
/// The reason we have to prove this thing.
/// FIXME: we shouldn't ignore the cause but instead change the affected visitors
/// to only visit predicates manually.
#[type_foldable(identity)]
#[type_visitable(ignore)]
pub cause: ObligationCause<'tcx>,

View file

@ -177,7 +177,11 @@ fn find_best_leaf_obligation<'tcx>(
)
.break_value()
.ok_or(())
// walk around the fact that the cause in `Obligation` is ignored by folders so that
// we can properly fudge the infer vars in cause code.
.map(|o| (o.cause.clone(), o))
})
.map(|(cause, o)| PredicateObligation { cause, ..o })
.unwrap_or(obligation);
deeply_normalize_for_diagnostics(infcx, obligation.param_env, obligation)
}

View file

@ -0,0 +1,43 @@
//@ compile-flags: -Znext-solver
//@ edition: 2024
//
// A regression test for the ICE variant in trait-system-refactor-initiative#245.
// We'll meet regions that're already popped off when using parent predicate in cause code.
// `cause` in `Obligation` is ignored by folders/visitors.
// In this case, `fudge_inference_if_ok` doesn't fudge a region var in cause code.
//
// The old solver doesn't trigger ICE because regions in the predicate are replaced with
// placeholders when checking generator witness. Besides, the old solver doesn't eagerly
// resolves vars before canonicalizing the predicate in `predicate_must_hold_modulo_regions`.
trait AsyncFn: Send + 'static {
type Fut: Future<Output = ()> + Send;
fn call(&self) -> Self::Fut;
}
async fn wrap_call<P: AsyncFn + ?Sized>(filter: &P) {
filter.call().await;
}
fn get_boxed_fn() -> Box<DynAsyncFnBoxed> {
todo!()
}
async fn cursed_fut() {
wrap_call(get_boxed_fn().as_ref()).await;
}
fn observe_fut_not_send() {
assert_send(cursed_fut());
//~^ ERROR: `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` cannot be shared between threads safely [E0277]
}
fn assert_send<T: Send>(t: T) -> T {
t
}
pub type BoxFuture<'a, T> = std::pin::Pin<Box<dyn Future<Output = T> + Send + 'a>>;
type DynAsyncFnBoxed = dyn AsyncFn<Fut = BoxFuture<'static, ()>>;
fn main() {}

View file

@ -0,0 +1,35 @@
error[E0277]: `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` cannot be shared between threads safely
--> $DIR/leaking-vars-in-cause-code-1.rs:32:17
|
LL | assert_send(cursed_fut());
| ----------- ^^^^^^^^^^^^ `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` cannot be shared between threads safely
| |
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>`
= note: required for `&dyn AsyncFn<Fut = Pin<Box<dyn Future<Output = ()> + Send>>>` to implement `Send`
note: required because it's used within this `async` fn body
--> $DIR/leaking-vars-in-cause-code-1.rs:19:53
|
LL | async fn wrap_call<P: AsyncFn + ?Sized>(filter: &P) {
| _____________________________________________________^
LL | | filter.call().await;
LL | | }
| |_^
note: required because it's used within this `async` fn body
--> $DIR/leaking-vars-in-cause-code-1.rs:27:23
|
LL | async fn cursed_fut() {
| _______________________^
LL | | wrap_call(get_boxed_fn().as_ref()).await;
LL | | }
| |_^
note: required by a bound in `assert_send`
--> $DIR/leaking-vars-in-cause-code-1.rs:36:19
|
LL | fn assert_send<T: Send>(t: T) -> T {
| ^^^^ required by this bound in `assert_send`
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -0,0 +1,31 @@
//@ compile-flags: -Znext-solver
// The `cause` in `Obligation` is ignored by type folders. So infer vars in cause code is not
// fudged.
// Check the comments of
// `leaking-vars-in-cause-code-1.rs` for more details.
trait Trait<T> {}
struct A<T>(T);
struct B<T>(T);
trait IncompleteGuidance {}
impl<T> Trait<()> for A<T>
where
T: IncompleteGuidance,
{
}
impl<T, U> Trait<()> for B<T>
//~^ ERROR: the type parameter `U` is not constrained by the impl trait, self type, or predicates
where
A<T>: Trait<U>,
{
}
fn impls_trait<T: Trait<()>>() {}
fn main() {
impls_trait::<B<()>>();
//~^ ERROR: the trait bound `(): IncompleteGuidance` is not satisfied
}

View file

@ -0,0 +1,37 @@
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
--> $DIR/leaking-vars-in-cause-code-2.rs:19:9
|
LL | impl<T, U> Trait<()> for B<T>
| ^ unconstrained type parameter
error[E0277]: the trait bound `(): IncompleteGuidance` is not satisfied
--> $DIR/leaking-vars-in-cause-code-2.rs:29:19
|
LL | impls_trait::<B<()>>();
| ^^^^^ the trait `IncompleteGuidance` is not implemented for `()`
|
help: this trait has no implementations, consider adding one
--> $DIR/leaking-vars-in-cause-code-2.rs:11:1
|
LL | trait IncompleteGuidance {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: required for `A<()>` to implement `Trait<()>`
--> $DIR/leaking-vars-in-cause-code-2.rs:13:9
|
LL | impl<T> Trait<()> for A<T>
| ^^^^^^^^^ ^^^^
LL | where
LL | T: IncompleteGuidance,
| ------------------ unsatisfied trait bound introduced here
= note: 1 redundant requirement hidden
= note: required for `B<()>` to implement `Trait<()>`
note: required by a bound in `impls_trait`
--> $DIR/leaking-vars-in-cause-code-2.rs:26:19
|
LL | fn impls_trait<T: Trait<()>>() {}
| ^^^^^^^^^ required by this bound in `impls_trait`
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0207, E0277.
For more information about an error, try `rustc --explain E0207`.