#![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
|
#![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
|
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:
|
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.
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
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
because of this hack we only try to prove
<Foo as FnOnce<()>>::Output == Foousingevaluate_obligationwhich usesDefiningAnchor::Bubbleinstead of theDefiningAnchor::Bindused by typeck and fulfill directly.The reason this breaks when using
DefiningAnchor::Bindis the following:we call
project_and_unify_typefor<Foo as FnOnce<()>>::Output == Foowhich normalizes<Foo as FnOnce<()>>::OutputtoFoo.The issue is that we then replace
Foowith a new inference variable (if we useDefiningAnchor::Bind)?nand add the item bounds ofFooas obligations on that new inference variable:rust/compiler/rustc_trait_selection/src/traits/project.rs
Lines 280 to 286 in c50c62d
This adds a new obligation
<?n as FnOnce<()>>::Output == Footo the fulfillment context, even though?nwas already constrained toFooagain. The easiest fix is to resolve inference variables in obligations before adding them to the fulfillment context.