diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 95f0c3dc9881d..29e0ab2605c95 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -950,7 +950,35 @@ impl<'a, 'tcx> FulfillProcessor<'a, 'tcx> { } match project::poly_project_and_unify_term(&mut self.selcx, &project_obligation) { - ProjectAndUnifyResult::Holds(os) => ProcessResult::Changed(mk_pending(obligation, os)), + ProjectAndUnifyResult::Holds(os) if os.is_empty() => { + ProcessResult::Changed(mk_pending(obligation, os)) + } + ProjectAndUnifyResult::Holds(os) => { + let input_projection_term = infcx + .resolve_vars_if_possible(project_obligation.predicate) + .map_bound(|p| p.projection_term); + let all_same_projection_term = os.iter().all(|o| { + let Some(proj_clause) = o.predicate.as_projection_clause() else { + return false; + }; + infcx.resolve_vars_if_possible(proj_clause).map_bound(|p| p.projection_term) + == input_projection_term + }); + if all_same_projection_term { + // Every nested obligation has the same projection term as the obligation + // we are processing, so registering would make fulfillment process the same + // obligation forever. This happens when unifying the projection with the + // predicate's term spawns a delayed copy of the predicate itself, see + // `InferCtxt::instantiate_var`. E.g. in Issue #159750, processing + // `<_ as Queryable>::Output == ?0` returns `Holds` with the single nested + // obligation `<_ as Queryable>::Output == ?1` where `?1` is merely unioned + // with `?0`. + // Since at this point the code will not compile, error immediately. + ProcessResult::Error(FulfillmentErrorCode::Ambiguity { overflow: None }) + } else { + ProcessResult::Changed(mk_pending(obligation, os)) + } + } ProjectAndUnifyResult::FailedNormalization => { stalled_on.clear(); stalled_on.extend(args_infer_vars( diff --git a/tests/ui/traits/projection-no-progress-cycle-issue-157516.rs b/tests/ui/traits/projection-no-progress-cycle-issue-157516.rs new file mode 100644 index 0000000000000..c66bf98391ee3 --- /dev/null +++ b/tests/ui/traits/projection-no-progress-cycle-issue-157516.rs @@ -0,0 +1,20 @@ +// Regression test for #157516: the same no-progress cycle as #159750, here +// reached through a GAT whose parameter can't be inferred. Check that we +// report an error instead of hanging. + +trait Bound {} + +trait Alloc { + const OPS: u32; +} + +trait Driver { + type Object: Alloc; +} + +fn f() -> u32 { + T::Object::OPS + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/traits/projection-no-progress-cycle-issue-157516.stderr b/tests/ui/traits/projection-no-progress-cycle-issue-157516.stderr new file mode 100644 index 0000000000000..a6960fc18493f --- /dev/null +++ b/tests/ui/traits/projection-no-progress-cycle-issue-157516.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/projection-no-progress-cycle-issue-157516.rs:16:5 + | +LL | T::Object::OPS + | ^^^^^^^^^ cannot infer type for type parameter `Ctx` declared on the associated type `Object` + | + = note: the type must implement `Bound` + = note: associated types cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl` +note: required by a bound in `Driver::Object` + --> $DIR/projection-no-progress-cycle-issue-157516.rs:12:22 + | +LL | type Object: Alloc; + | ^^^^^ required by this bound in `Driver::Object` +help: use the fully qualified path to an implementation + | +LL - T::Object::OPS +LL + ::Object::OPS + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/traits/projection-no-progress-cycle-issue-159750.rs b/tests/ui/traits/projection-no-progress-cycle-issue-159750.rs new file mode 100644 index 0000000000000..bfb793e7f7046 --- /dev/null +++ b/tests/ui/traits/projection-no-progress-cycle-issue-159750.rs @@ -0,0 +1,30 @@ +// Regression test for #159750. When `::Assoc` can't be resolved +// to a concrete type, equating it with an inference variable spawns a copy of +// the obligation being processed, which fulfillment used to treat as progress +// and loops forever. Check that we report an error instead of hanging. + +trait Container { + type IDContainer; +} + +impl Container for Option { + type IDContainer = (); +} + +trait Queryable { + type Output; + type Container; + + fn finish(&self) -> >::IDContainer; + //~^ ERROR the trait bound `::Container: Container<::Output>` is not satisfied +} + +fn follow() -> impl Queryable> { + //~^ ERROR the trait bound `(): Queryable` is not satisfied + loop {} +} + +fn main() { + follow().finish(); + //~^ ERROR type annotations needed +} diff --git a/tests/ui/traits/projection-no-progress-cycle-issue-159750.stderr b/tests/ui/traits/projection-no-progress-cycle-issue-159750.stderr new file mode 100644 index 0000000000000..6c9c76d6bd5d4 --- /dev/null +++ b/tests/ui/traits/projection-no-progress-cycle-issue-159750.stderr @@ -0,0 +1,35 @@ +error[E0277]: the trait bound `::Container: Container<::Output>` is not satisfied + --> $DIR/projection-no-progress-cycle-issue-159750.rs:18:25 + | +LL | fn finish(&self) -> >::IDContainer; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Container<::Output>` is not implemented for `::Container` + | +help: consider further restricting the associated type + | +LL | fn finish(&self) -> >::IDContainer where ::Container: Container<::Output>; + | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +error[E0277]: the trait bound `(): Queryable` is not satisfied + --> $DIR/projection-no-progress-cycle-issue-159750.rs:22:19 + | +LL | fn follow() -> impl Queryable> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Queryable` is not implemented for `()` + | +help: this trait has no implementations, consider adding one + --> $DIR/projection-no-progress-cycle-issue-159750.rs:14:1 + | +LL | trait Queryable { + | ^^^^^^^^^^^^^^^ + +error[E0284]: type annotations needed + --> $DIR/projection-no-progress-cycle-issue-159750.rs:28:14 + | +LL | follow().finish(); + | ^^^^^^ + | + = note: cannot satisfy `> as Queryable>::Output == _` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0284. +For more information about an error, try `rustc --explain E0277`.