Skip to content
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

infinite recursion with TAIT and replace_opaque_types_with_inference_vars in project #109268

Open
lcnr opened this issue Mar 17, 2023 · 5 comments
Labels
C-bug Category: This is a bug. E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` S-bug-has-test Status: This bug is tracked inside the repo by a `known-bug` test. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@lcnr
Copy link
Contributor

lcnr commented Mar 17, 2023

#![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.

@lcnr lcnr added C-bug Category: This is a bug. F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` T-types Relevant to the types team, which will review and decide on the PR/issue. labels Mar 17, 2023
@compiler-errors
Copy link
Member

The easiest fix is to resolve inference variables in obligations before adding them to the fulfillment context.

We already do this though, unless you meant something different--

impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
fn register_predicate_obligation(
&mut self,
infcx: &InferCtxt<'tcx>,
obligation: PredicateObligation<'tcx>,
) {
// this helps to reduce duplicate errors, as well as making
// debug output much nicer to read and so on.
let obligation = infcx.resolve_vars_if_possible(obligation);
debug!(?obligation, "register_predicate_obligation");

@compiler-errors
Copy link
Member

Ah, but ObligationForest::register_obligation_at skips over that code, oof

@oli-obk
Copy link
Contributor

oli-obk commented Jul 26, 2023

This now reports an overflow instead of freezing rustc or otherwise crashing. It's still wrong, but easier to debug

@oli-obk oli-obk moved this from Todo to In Progress in type alias impl trait stabilization Aug 7, 2023
@oli-obk oli-obk moved this from In Progress to Can do after stabilization in type alias impl trait stabilization Aug 11, 2023
@jackh726 jackh726 added the S-bug-has-test Status: This bug is tracked inside the repo by a `known-bug` test. label Sep 22, 2024
@matthiaskrgr
Copy link
Member

seems this does not hang anymore
rustc 1.84.0-nightly (59cec72a5 2024-11-08)

@compiler-errors compiler-errors added the E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc label Nov 9, 2024
@moxian
Copy link
Contributor

moxian commented Mar 20, 2025

Originally regressed (no hang -> hang) in nightly-2022-08-19, but I'm unable to pinpoint the specific PR

cargo-bisect-rustc output

Regression in nightly-2022-08-19


fetching https://static.rust-lang.org/dist/2022-08-18/channel-rust-nightly-git-commit-hash.txt
nightly manifest 2022-08-18: 40 B / 40 B [=======================================================] 100.00 % 46.06 KB/s converted 2022-08-18 to 9c20b2a
fetching https://static.rust-lang.org/dist/2022-08-19/channel-rust-nightly-git-commit-hash.txt
nightly manifest 2022-08-19: 40 B / 40 B [=======================================================] 100.00 % 68.98 KB/s converted 2022-08-19 to 0b79f75
looking for regression commit between 2022-08-18 and 2022-08-19
fetching (via remote github) commits from max(9c20b2a, 2022-08-16) to 0b79f75
ending github query because we found starting sha: 9c20b2a
get_commits_between returning commits, len: 6

ERROR: no CI builds available between 9c20b2a and 0b79f75 within last 167 days

unregressed (hang -> no hang) in nightly-2023-06-18 / f3b7dd6 / #108860

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. E-needs-bisection Call for participation: This issue needs bisection: https://github.com/rust-lang/cargo-bisect-rustc F-type_alias_impl_trait `#[feature(type_alias_impl_trait)]` S-bug-has-test Status: This bug is tracked inside the repo by a `known-bug` test. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
Status: Can do after stabilization
Development

Successfully merging a pull request may close this issue.

6 participants