-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Description
#![feature(type_alias_impl_trait)]
type Foo<'a> = impl Fn() -> Foo<'a>;
fn crash<'a>(_: &'a (), x: Foo<'a>) -> Foo<'a> {
x
}
fn main() {}this causes rustc to freeze. This is similar to
rust/tests/ui/type-alias-impl-trait/issue-53398-cyclic-types.rs
Lines 1 to 10 in c50c62d
| #![feature(type_alias_impl_trait)] | |
| type Foo = impl Fn() -> Foo; | |
| fn foo() -> Foo { | |
| //~^ ERROR: overflow evaluating the requirement | |
| foo | |
| } | |
| fn main() {} |
The only reason that this test does not hang is the following hack in fulfillment
rust/compiler/rustc_trait_selection/src/traits/fulfill.rs
Lines 711 to 733 in c50c62d
| if obligation.predicate.is_global() { | |
| // no type variables present, can use evaluation for better caching. | |
| // FIXME: consider caching errors too. | |
| if self.selcx.infcx.predicate_must_hold_considering_regions(obligation) { | |
| if let Some(key) = ProjectionCacheKey::from_poly_projection_predicate( | |
| &mut self.selcx, | |
| project_obligation.predicate, | |
| ) { | |
| // If `predicate_must_hold_considering_regions` succeeds, then we've | |
| // evaluated all sub-obligations. We can therefore mark the 'root' | |
| // obligation as complete, and skip evaluating sub-obligations. | |
| self.selcx | |
| .infcx | |
| .inner | |
| .borrow_mut() | |
| .projection_cache() | |
| .complete(key, EvaluationResult::EvaluatedToOk); | |
| } | |
| return ProcessResult::Changed(vec![]); | |
| } else { | |
| debug!("Does NOT hold: {:?}", obligation); | |
| } | |
| } |
because of this hack we only try to prove <Foo as FnOnce<()>>::Output == Foo using evaluate_obligation which uses DefiningAnchor::Bubble instead of the DefiningAnchor::Bind used by typeck and fulfill directly.
The reason this breaks when using DefiningAnchor::Bind is the following:
we call project_and_unify_type for <Foo as FnOnce<()>>::Output == Foo which normalizes <Foo as FnOnce<()>>::Output to Foo.
The issue is that we then replace Foo with a new inference variable (if we use DefiningAnchor::Bind) ?n and add the item bounds of Foo as obligations on that new inference variable:
rust/compiler/rustc_trait_selection/src/traits/project.rs
Lines 280 to 286 in c50c62d
| let InferOk { value: actual, obligations: new } = | |
| selcx.infcx.replace_opaque_types_with_inference_vars( | |
| actual, | |
| obligation.cause.body_id, | |
| obligation.cause.span, | |
| obligation.param_env, | |
| ); |
This adds a new obligation <?n as FnOnce<()>>::Output == Foo to the fulfillment context, even though ?n was already constrained to Foo again. The easiest fix is to resolve inference variables in obligations before adding them to the fulfillment context.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status