summaryrefslogtreecommitdiffstats
path: root/tests/ui/traits/next-solver/cycles
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-19 09:26:03 +0000
commit9918693037dce8aa4bb6f08741b6812923486c18 (patch)
tree21d2b40bec7e6a7ea664acee056eb3d08e15a1cf /tests/ui/traits/next-solver/cycles
parentReleasing progress-linux version 1.75.0+dfsg1-5~progress7.99u1. (diff)
downloadrustc-9918693037dce8aa4bb6f08741b6812923486c18.tar.xz
rustc-9918693037dce8aa4bb6f08741b6812923486c18.zip
Merging upstream version 1.76.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/ui/traits/next-solver/cycles')
-rw-r--r--tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.rs35
-rw-r--r--tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr16
-rw-r--r--tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs69
-rw-r--r--tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr16
-rw-r--r--tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs37
-rw-r--r--tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr29
-rw-r--r--tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs53
-rw-r--r--tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr10
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs48
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr16
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs44
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs36
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.rs33
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr16
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs46
-rw-r--r--tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr29
-rw-r--r--tests/ui/traits/next-solver/cycles/leak-check-coinductive-cycle.rs33
-rw-r--r--tests/ui/traits/next-solver/cycles/provisional-result-done.rs33
18 files changed, 599 insertions, 0 deletions
diff --git a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.rs b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.rs
new file mode 100644
index 000000000..947b52da7
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.rs
@@ -0,0 +1,35 @@
+// compile-flags: -Znext-solver
+
+// Proving `W<?0>: Trait` instantiates `?0` with `(W<?1>, W<?2>)` and then
+// proves `W<?1>: Trait` and `W<?2>: Trait`, resulting in a coinductive cycle.
+//
+// Proving coinductive cycles runs until we reach a fixpoint. However, after
+// computing `try_evaluate_added_goals` in the second fixpoint iteration, the
+// self type already has a depth equal to the number of steps. This results
+// in enormous constraints, causing the canonicalizer to hang without ever
+// reaching the recursion limit. We currently avoid that by erasing the constraints
+// from overflow.
+//
+// This previously caused a hang in the trait solver, see
+// https://github.com/rust-lang/trait-system-refactor-initiative/issues/13.
+
+#![feature(rustc_attrs)]
+
+#[rustc_coinductive]
+trait Trait {}
+
+struct W<T>(T);
+
+impl<T, U> Trait for W<(W<T>, W<U>)>
+where
+ W<T>: Trait,
+ W<U>: Trait,
+{
+}
+
+fn impls<T: Trait>() {}
+
+fn main() {
+ impls::<W<_>>();
+ //~^ ERROR overflow evaluating the requirement
+}
diff --git a/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr
new file mode 100644
index 000000000..150100f2c
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/coinduction/fixpoint-exponential-growth.stderr
@@ -0,0 +1,16 @@
+error[E0275]: overflow evaluating the requirement `W<_>: Trait`
+ --> $DIR/fixpoint-exponential-growth.rs:33:13
+ |
+LL | impls::<W<_>>();
+ | ^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`)
+note: required by a bound in `impls`
+ --> $DIR/fixpoint-exponential-growth.rs:30:13
+ |
+LL | fn impls<T: Trait>() {}
+ | ^^^^^ required by this bound in `impls`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs
new file mode 100644
index 000000000..a3c07b987
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.rs
@@ -0,0 +1,69 @@
+// compile-flags: -Znext-solver
+#![feature(rustc_attrs)]
+
+// This test is incredibly subtle. At its core the goal is to get a coinductive cycle,
+// which, depending on its root goal, either holds or errors. We achieve this by getting
+// incomplete inference via a `ParamEnv` candidate in the `A<T>` impl and required
+// inference from an `Impl` candidate in the `B<T>` impl.
+//
+// To make global cache accesses stronger than the guidance from the where-bounds, we add
+// another coinductive cycle from `A<T>: Trait<U, V, D>` to `A<T>: Trait<U, D, V>` and only
+// constrain `D` directly. This means that any candidates which rely on `V` only make
+// progress in the second iteration, allowing a cache access in the first iteration to take
+// precedence.
+//
+// tl;dr: our caching of coinductive cycles was broken and this is a regression
+// test for that.
+
+#[rustc_coinductive]
+trait Trait<T: ?Sized, V: ?Sized, D: ?Sized> {}
+struct A<T: ?Sized>(*const T);
+struct B<T: ?Sized>(*const T);
+
+trait IncompleteGuidance<T: ?Sized, V: ?Sized> {}
+impl<T: ?Sized, U: ?Sized + 'static> IncompleteGuidance<U, u8> for T {}
+impl<T: ?Sized, U: ?Sized + 'static> IncompleteGuidance<U, i8> for T {}
+impl<T: ?Sized, U: ?Sized + 'static> IncompleteGuidance<U, i16> for T {}
+
+trait ImplGuidance<T: ?Sized, V: ?Sized> {}
+impl<T: ?Sized> ImplGuidance<u32, u8> for T {}
+impl<T: ?Sized> ImplGuidance<i32, i8> for T {}
+
+impl<T: ?Sized, U: ?Sized, V: ?Sized, D: ?Sized> Trait<U, V, D> for A<T>
+where
+ T: IncompleteGuidance<U, V>,
+ A<T>: Trait<U, D, V>,
+ B<T>: Trait<U, V, D>,
+ (): ToU8<D>,
+{
+}
+
+trait ToU8<T: ?Sized> {}
+impl ToU8<u8> for () {}
+
+impl<T: ?Sized, U: ?Sized, V: ?Sized, D: ?Sized> Trait<U, V, D> for B<T>
+where
+ T: ImplGuidance<U, V>,
+ A<T>: Trait<U, V, D>,
+{
+}
+
+fn impls_trait<T: ?Sized + Trait<U, V, D>, U: ?Sized, V: ?Sized, D: ?Sized>() {}
+
+fn with_bound<X>()
+where
+ X: IncompleteGuidance<i32, u8>,
+ X: IncompleteGuidance<u32, i8>,
+ X: IncompleteGuidance<u32, i16>,
+{
+ impls_trait::<B<X>, _, _, _>(); // entering the cycle from `B` works
+
+ // entering the cycle from `A` fails, but would work if we were to use the cache
+ // result of `B<X>`.
+ impls_trait::<A<X>, _, _, _>();
+ //~^ ERROR the trait bound `A<X>: Trait<_, _, _>` is not satisfied
+}
+
+fn main() {
+ with_bound::<u32>();
+}
diff --git a/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr
new file mode 100644
index 000000000..d49321917
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/coinduction/incompleteness-unstable-result.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the trait bound `A<X>: Trait<_, _, _>` is not satisfied
+ --> $DIR/incompleteness-unstable-result.rs:63:19
+ |
+LL | impls_trait::<A<X>, _, _, _>();
+ | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A<X>`
+ |
+ = help: the trait `Trait<U, V, D>` is implemented for `A<T>`
+note: required by a bound in `impls_trait`
+ --> $DIR/incompleteness-unstable-result.rs:51:28
+ |
+LL | fn impls_trait<T: ?Sized + Trait<U, V, D>, U: ?Sized, V: ?Sized, D: ?Sized>() {}
+ | ^^^^^^^^^^^^^^ required by this bound in `impls_trait`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs
new file mode 100644
index 000000000..0f19bc2c5
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.rs
@@ -0,0 +1,37 @@
+// compile-flags: -Znext-solver
+#![feature(rustc_attrs)]
+
+// Test that having both an inductive and a coinductive cycle
+// is handled correctly.
+
+#[rustc_coinductive]
+trait Trait {}
+impl<T: Inductive + Coinductive> Trait for T {}
+
+trait Inductive {}
+impl<T: Trait> Inductive for T {}
+#[rustc_coinductive]
+trait Coinductive {}
+impl<T: Trait> Coinductive for T {}
+
+fn impls_trait<T: Trait>() {}
+
+#[rustc_coinductive]
+trait TraitRev {}
+impl<T: CoinductiveRev + InductiveRev> TraitRev for T {}
+
+trait InductiveRev {}
+impl<T: TraitRev> InductiveRev for T {}
+#[rustc_coinductive]
+trait CoinductiveRev {}
+impl<T: TraitRev> CoinductiveRev for T {}
+
+fn impls_trait_rev<T: TraitRev>() {}
+
+fn main() {
+ impls_trait::<()>();
+ //~^ ERROR overflow evaluating the requirement
+
+ impls_trait_rev::<()>();
+ //~^ ERROR overflow evaluating the requirement
+}
diff --git a/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr
new file mode 100644
index 000000000..a3404da51
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/double-cycle-inductive-coinductive.stderr
@@ -0,0 +1,29 @@
+error[E0275]: overflow evaluating the requirement `(): Trait`
+ --> $DIR/double-cycle-inductive-coinductive.rs:32:19
+ |
+LL | impls_trait::<()>();
+ | ^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`)
+note: required by a bound in `impls_trait`
+ --> $DIR/double-cycle-inductive-coinductive.rs:17:19
+ |
+LL | fn impls_trait<T: Trait>() {}
+ | ^^^^^ required by this bound in `impls_trait`
+
+error[E0275]: overflow evaluating the requirement `(): TraitRev`
+ --> $DIR/double-cycle-inductive-coinductive.rs:35:23
+ |
+LL | impls_trait_rev::<()>();
+ | ^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`)
+note: required by a bound in `impls_trait_rev`
+ --> $DIR/double-cycle-inductive-coinductive.rs:29:23
+ |
+LL | fn impls_trait_rev<T: TraitRev>() {}
+ | ^^^^^^^^ required by this bound in `impls_trait_rev`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
new file mode 100644
index 000000000..c7e2e2d5e
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
@@ -0,0 +1,53 @@
+// compile-flags: -Znext-solver
+#![feature(rustc_attrs)]
+
+// Check that we correctly rerun the trait solver for heads of cycles,
+// even if they are not the root.
+
+struct A<T: ?Sized>(*const T);
+struct B<T: ?Sized>(*const T);
+struct C<T: ?Sized>(*const T);
+
+#[rustc_coinductive]
+trait Trait<'a, 'b> {}
+trait NotImplemented {}
+
+impl<'a, 'b, T: ?Sized> Trait<'a, 'b> for A<T> where B<T>: Trait<'a, 'b> {}
+
+// With this the root of `B<T>` is `A<T>`, even if the other impl does
+// not have a cycle with `A<T>`. This candidate never applies because of
+// the `A<T>: NotImplemented` bound.
+impl<'a, 'b, T: ?Sized> Trait<'a, 'b> for B<T>
+where
+ A<T>: Trait<'a, 'b>,
+ A<T>: NotImplemented,
+{
+}
+
+// This impl directly requires 'b to be equal to 'static.
+//
+// Because of the coinductive cycle through `C<T>` it also requires
+// 'a to be 'static.
+impl<'a, T: ?Sized> Trait<'a, 'static> for B<T>
+where
+ C<T>: Trait<'a, 'a>,
+{}
+
+// In the first iteration of `B<T>: Trait<'a, 'b>` we don't add any
+// constraints here, only after setting the provisional result to require
+// `'b == 'static` do we also add that constraint for `'a`.
+impl<'a, 'b, T: ?Sized> Trait<'a, 'b> for C<T>
+where
+ B<T>: Trait<'a, 'b>,
+{}
+
+fn impls_trait<'a, 'b, T: Trait<'a, 'b> + ?Sized>() {}
+
+fn check<'a, T: ?Sized>() {
+ impls_trait::<'a, 'static, A<T>>();
+ //~^ ERROR lifetime may not live long enough
+}
+
+fn main() {
+ check::<()>();
+}
diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr
new file mode 100644
index 000000000..7b3075f4f
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+ --> $DIR/fixpoint-rerun-all-cycle-heads.rs:47:5
+ |
+LL | fn check<'a, T: ?Sized>() {
+ | -- lifetime `'a` defined here
+LL | impls_trait::<'a, 'static, A<T>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs
new file mode 100644
index 000000000..fdc7afea3
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.rs
@@ -0,0 +1,48 @@
+// compile-flags: -Znext-solver
+#![feature(trivial_bounds, marker_trait_attr)]
+#![allow(trivial_bounds)]
+// This previously triggered a bug in the provisional cache.
+//
+// This has the proof tree
+// - `MultipleCandidates: Trait` proven via impl-one
+// - `MultipleNested: Trait` via impl
+// - `MultipleCandidates: Trait` (inductive cycle ~> OVERFLOW)
+// - `DoesNotImpl: Trait` (ERR)
+// - `MultipleCandidates: Trait` proven via impl-two
+// - `MultipleNested: Trait` (in provisional cache ~> OVERFLOW)
+//
+// We previously incorrectly treated the `MultipleCandidates: Trait` as
+// overflow because it was in the cache and reached via an inductive cycle.
+// It should be `NoSolution`.
+
+struct MultipleCandidates;
+struct MultipleNested;
+struct DoesNotImpl;
+
+#[marker]
+trait Trait {}
+
+// impl-one
+impl Trait for MultipleCandidates
+where
+ MultipleNested: Trait
+{}
+
+// impl-two
+impl Trait for MultipleCandidates
+where
+ MultipleNested: Trait,
+{}
+
+impl Trait for MultipleNested
+where
+ MultipleCandidates: Trait,
+ DoesNotImpl: Trait,
+{}
+
+fn impls_trait<T: Trait>() {}
+
+fn main() {
+ impls_trait::<MultipleCandidates>();
+ //~^ ERROR the trait bound `MultipleCandidates: Trait` is not satisfied
+}
diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr
new file mode 100644
index 000000000..acacaf6a3
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-err.stderr
@@ -0,0 +1,16 @@
+error[E0277]: the trait bound `MultipleCandidates: Trait` is not satisfied
+ --> $DIR/inductive-cycle-but-err.rs:46:19
+ |
+LL | impls_trait::<MultipleCandidates>();
+ | ^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `MultipleCandidates`
+ |
+ = help: the trait `Trait` is implemented for `MultipleCandidates`
+note: required by a bound in `impls_trait`
+ --> $DIR/inductive-cycle-but-err.rs:43:19
+ |
+LL | fn impls_trait<T: Trait>() {}
+ | ^^^^^ required by this bound in `impls_trait`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs
new file mode 100644
index 000000000..d6d9762bb
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-but-ok.rs
@@ -0,0 +1,44 @@
+// compile-flags: -Znext-solver
+// check-pass
+#![feature(trivial_bounds, marker_trait_attr)]
+#![allow(trivial_bounds)]
+
+// This previously triggered a bug in the provisional cache.
+//
+// This has the proof tree
+// - `Root: Trait` proven via impl
+// - `MultipleCandidates: Trait`
+// - candidate: overflow-impl
+// - `Root: Trait` (inductive cycle ~> OVERFLOW)
+// - candidate: trivial-impl ~> YES
+// - merge respones ~> YES
+// - `MultipleCandidates: Trait` (in provisional cache ~> OVERFLOW)
+//
+// We previously incorrectly treated the `MultipleCandidates: Trait` as
+// overflow because it was in the cache and reached via an inductive cycle.
+// It should be `YES`.
+
+struct Root;
+struct MultipleCandidates;
+
+#[marker]
+trait Trait {}
+impl Trait for Root
+where
+ MultipleCandidates: Trait,
+ MultipleCandidates: Trait,
+{}
+
+// overflow-impl
+impl Trait for MultipleCandidates
+where
+ Root: Trait,
+{}
+// trivial-impl
+impl Trait for MultipleCandidates {}
+
+fn impls_trait<T: Trait>() {}
+
+fn main() {
+ impls_trait::<Root>();
+}
diff --git a/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs b/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs
new file mode 100644
index 000000000..a32f7a13a
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs
@@ -0,0 +1,36 @@
+// check-pass
+// compile-flags: -Znext-solver
+#![feature(rustc_attrs, marker_trait_attr)]
+#[rustc_coinductive]
+trait Trait {}
+
+impl<T, U> Trait for (T, U)
+where
+ (U, T): Trait,
+ (T, U): Inductive,
+ (): ConstrainToU32<T>,
+{}
+
+trait ConstrainToU32<T> {}
+impl ConstrainToU32<u32> for () {}
+
+// We only prefer the candidate without an inductive cycle
+// once the inductive cycle has the same constraints as the
+// other goal.
+#[marker]
+trait Inductive {}
+impl<T, U> Inductive for (T, U)
+where
+ (T, U): Trait,
+{}
+
+impl Inductive for (u32, u32) {}
+
+fn impls_trait<T, U>()
+where
+ (T, U): Trait,
+{}
+
+fn main() {
+ impls_trait::<_, _>();
+}
diff --git a/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.rs b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.rs
new file mode 100644
index 000000000..efeb8d023
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.rs
@@ -0,0 +1,33 @@
+// compile-flags: -Znext-solver
+
+// This currently hangs if we do not erase constraints from
+// overflow.
+//
+// We set the provisional result of `W<?0>` to `?0 := W<_>`.
+// The next iteration does not simply result in a `?0 := W<W<_>` constraint as
+// one might expect, but instead each time we evaluate the nested `W<T>` goal we
+// apply the previously returned constraints: the first fixpoint iteration goes
+// as follows: `W<?1>: Trait` constrains `?1` to `W<?2>`, we then evaluate
+// `W<W<?2>>: Trait` the next time we try to prove the nested goal. This results
+// inn `W<W<W<?3>>>` and so on. This goes on until we reach overflow in
+// `try_evaluate_added_goals`. This means the provisional result after the
+// second fixpoint iteration is already `W<W<W<...>>>` with a size proportional
+// to the number of steps in `try_evaluate_added_goals`. The size then continues
+// to grow. The exponential blowup from having 2 nested goals per impl causes
+// the solver to hang without hitting the recursion limit.
+trait Trait {}
+
+struct W<T: ?Sized>(*const T);
+
+impl<T: ?Sized> Trait for W<W<T>>
+where
+ W<T>: Trait,
+ W<T>: Trait,
+{}
+
+fn impls_trait<T: Trait>() {}
+
+fn main() {
+ impls_trait::<W<_>>();
+ //~^ ERROR overflow evaluating the requirement
+}
diff --git a/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr
new file mode 100644
index 000000000..424519207
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-fixpoint-hang.stderr
@@ -0,0 +1,16 @@
+error[E0275]: overflow evaluating the requirement `W<_>: Trait`
+ --> $DIR/inductive-fixpoint-hang.rs:31:19
+ |
+LL | impls_trait::<W<_>>();
+ | ^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_fixpoint_hang`)
+note: required by a bound in `impls_trait`
+ --> $DIR/inductive-fixpoint-hang.rs:28:19
+ |
+LL | fn impls_trait<T: Trait>() {}
+ | ^^^^^ required by this bound in `impls_trait`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs
new file mode 100644
index 000000000..f2f6e009d
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.rs
@@ -0,0 +1,46 @@
+// compile-flags: -Znext-solver
+#![feature(rustc_attrs, trivial_bounds)]
+
+// We have to be careful here:
+//
+// We either have the provisional result of `A -> B -> A` on the
+// stack, which is a fully coinductive cycle. Accessing the
+// provisional result for `B` as part of the `A -> C -> B -> A` cycle
+// has to make sure we don't just use the result of `A -> B -> A` as the
+// new cycle is inductive.
+//
+// Alternatively, if we have `A -> C -> A` first, then `A -> B -> A` has
+// a purely inductive stack, so something could also go wrong here.
+
+#[rustc_coinductive]
+trait A {}
+#[rustc_coinductive]
+trait B {}
+trait C {}
+
+impl<T: B + C> A for T {}
+impl<T: A> B for T {}
+impl<T: B> C for T {}
+
+fn impls_a<T: A>() {}
+
+// The same test with reordered where clauses to make sure we're actually testing anything.
+#[rustc_coinductive]
+trait AR {}
+#[rustc_coinductive]
+trait BR {}
+trait CR {}
+
+impl<T: CR + BR> AR for T {}
+impl<T: AR> BR for T {}
+impl<T: BR> CR for T {}
+
+fn impls_ar<T: AR>() {}
+
+fn main() {
+ impls_a::<()>();
+ //~^ ERROR overflow evaluating the requirement `(): A`
+
+ impls_ar::<()>();
+ //~^ ERROR overflow evaluating the requirement `(): AR`
+}
diff --git a/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr
new file mode 100644
index 000000000..859b3f3f1
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/inductive-not-on-stack.stderr
@@ -0,0 +1,29 @@
+error[E0275]: overflow evaluating the requirement `(): A`
+ --> $DIR/inductive-not-on-stack.rs:41:15
+ |
+LL | impls_a::<()>();
+ | ^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`)
+note: required by a bound in `impls_a`
+ --> $DIR/inductive-not-on-stack.rs:25:15
+ |
+LL | fn impls_a<T: A>() {}
+ | ^ required by this bound in `impls_a`
+
+error[E0275]: overflow evaluating the requirement `(): AR`
+ --> $DIR/inductive-not-on-stack.rs:44:16
+ |
+LL | impls_ar::<()>();
+ | ^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`)
+note: required by a bound in `impls_ar`
+ --> $DIR/inductive-not-on-stack.rs:38:16
+ |
+LL | fn impls_ar<T: AR>() {}
+ | ^^ required by this bound in `impls_ar`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/cycles/leak-check-coinductive-cycle.rs b/tests/ui/traits/next-solver/cycles/leak-check-coinductive-cycle.rs
new file mode 100644
index 000000000..9ff362ec8
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/leak-check-coinductive-cycle.rs
@@ -0,0 +1,33 @@
+// compile-flags: -Znext-solver
+// check-pass
+#![feature(rustc_attrs)]
+
+#[rustc_coinductive]
+trait Trait<T> {}
+impl<'a, 'b, T> Trait<T> for (&'a (), &'b ())
+where
+ 'b: 'a,
+ &'a (): Trait<T>,
+{}
+
+impl Trait<i32> for &'static () {}
+impl<'a> Trait<u32> for &'a ()
+where
+ for<'b> (&'a (), &'b ()): Trait<u32>,
+{}
+
+
+fn impls_trait<T: Trait<U>, U>() {}
+
+fn main() {
+ // This infers to `impls_trait::<(&'static (), &'static ()), i32>();`
+ //
+ // In the first attempt we have 2 candidates for `&'a (): Trait<_>`
+ // and we get ambiguity. The result is therefore ambiguity with a `'b: 'a`
+ // constraint. The next attempt then uses that provisional result when
+ // trying to apply `impl<'a> Trait<u32> for &'a ()`. This means we get a
+ // `for<'b> 'b: 'a` bound which fails the leak check. Because of this we
+ // end up with a single impl for `&'a (): Trait<_>` which infers `_` to `i32`
+ // and succeeds.
+ impls_trait::<(&(), &()), _>();
+}
diff --git a/tests/ui/traits/next-solver/cycles/provisional-result-done.rs b/tests/ui/traits/next-solver/cycles/provisional-result-done.rs
new file mode 100644
index 000000000..0f3b84ce5
--- /dev/null
+++ b/tests/ui/traits/next-solver/cycles/provisional-result-done.rs
@@ -0,0 +1,33 @@
+// compile-flags: -Znext-solver
+// check-pass
+
+// This tests checks that we update results in the provisional cache when
+// we pop a goal from the stack.
+#![feature(auto_traits)]
+auto trait Coinductive {}
+struct Foo<T>(T);
+struct Bar<T>(T);
+
+impl<T> Coinductive for Foo<T>
+where
+ Bar<T>: Coinductive
+{}
+
+impl<T> Coinductive for Bar<T>
+where
+ Foo<T>: Coinductive,
+ Bar<T>: ConstrainInfer,
+{}
+
+trait ConstrainInfer {}
+impl ConstrainInfer for Bar<u8> {}
+impl ConstrainInfer for Foo<u16> {}
+
+fn impls<T: Coinductive>() -> T { todo!() }
+
+fn constrain<T: ConstrainInfer>(_: T) {}
+
+fn main() {
+ // This should constrain `_` to `u8`.
+ impls::<Foo<_>>();
+}