about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-04-25 17:57:59 +0000
committerMichael Goulet <michael@errs.io>2025-04-25 18:14:39 +0000
commit31c8d10342bfa437d23eb4aa3f9c807a67f14272 (patch)
tree9d756d5a1c61c6c838fee5660108145caeb1e056
parent8f43b85954d2f3d8fc00a7504c603e5ca9eb0695 (diff)
downloadrust-31c8d10342bfa437d23eb4aa3f9c807a67f14272.tar.gz
rust-31c8d10342bfa437d23eb4aa3f9c807a67f14272.zip
Track per-obligation recursion depth only if there is inference
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs23
-rw-r--r--tests/ui/traits/next-solver/coerce-depth.rs31
2 files changed, 46 insertions, 8 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 848d0646d00..1f4fa5aac10 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -163,15 +163,15 @@ where
     fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> {
         assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
         let mut errors = Vec::new();
-        for i in 0.. {
-            if !infcx.tcx.recursion_limit().value_within_limit(i) {
-                self.obligations.on_fulfillment_overflow(infcx);
-                // Only return true errors that we have accumulated while processing.
-                return errors;
-            }
-
+        loop {
             let mut has_changed = false;
-            for obligation in self.obligations.drain_pending(|_| true) {
+            for mut obligation in self.obligations.drain_pending(|_| true) {
+                if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) {
+                    self.obligations.on_fulfillment_overflow(infcx);
+                    // Only return true errors that we have accumulated while processing.
+                    return errors;
+                }
+
                 let goal = obligation.as_goal();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
                     .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
@@ -189,6 +189,13 @@ where
                 };
 
                 if changed == HasChanged::Yes {
+                    // We increment the recursion depth here to track the number of times
+                    // this goal has resulted in inference progress. This doesn't precisely
+                    // model the way that we track recursion depth in the old solver due
+                    // to the fact that we only process root obligations, but it is a good
+                    // approximation and should only result in fulfillment overflow in
+                    // pathological cases.
+                    obligation.recursion_depth += 1;
                     has_changed = true;
                 }
 
diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coerce-depth.rs
new file mode 100644
index 00000000000..c8fc3fcab59
--- /dev/null
+++ b/tests/ui/traits/next-solver/coerce-depth.rs
@@ -0,0 +1,31 @@
+//@ check-pass
+//@ compile-flags: -Znext-solver
+
+// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed
+// in *reverse* order, which may require O(N) iterations of the fulfillment loop.
+
+#![recursion_limit = "16"]
+
+fn main() {
+    match 0 {
+        0 => None,
+        1 => None,
+        2 => None,
+        3 => None,
+        4 => None,
+        5 => None,
+        6 => None,
+        7 => None,
+        8 => None,
+        9 => None,
+        10 => None,
+        11 => None,
+        12 => None,
+        13 => None,
+        14 => None,
+        15 => None,
+        16 => None,
+        17 => None,
+        _ => Some(1u32),
+    };
+}