about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs63
-rw-r--r--src/test/incremental/issue-92987-provisional-dep-node.rs24
2 files changed, 75 insertions, 12 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 558ecd393b6..ab4fb9607ca 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -765,14 +765,38 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             debug!(?result, "CACHE MISS");
             self.insert_evaluation_cache(param_env, fresh_trait_pred, dep_node, result);
 
-            stack.cache().on_completion(stack.dfn, |fresh_trait_pred, provisional_result| {
-                self.insert_evaluation_cache(
-                    param_env,
-                    fresh_trait_pred,
-                    dep_node,
-                    provisional_result.max(result),
-                );
-            });
+            stack.cache().on_completion(
+                stack.dfn,
+                |fresh_trait_pred, provisional_result, provisional_dep_node| {
+                    // Create a new `DepNode` that has dependencies on:
+                    // * The `DepNode` for the original evaluation that resulted in a provisional cache
+                    // entry being crated
+                    // * The `DepNode` for the *current* evaluation, which resulted in us completing
+                    // provisional caches entries and inserting them into the evaluation cache
+                    //
+                    // This ensures that when a query reads this entry from the evaluation cache,
+                    // it will end up (transitively) dependening on all of the incr-comp dependencies
+                    // created during the evaluation of this trait. For example, evaluating a trait
+                    // will usually require us to invoke `type_of(field_def_id)` to determine the
+                    // constituent types, and we want any queries reading from this evaluation
+                    // cache entry to end up with a transitive `type_of(field_def_id`)` dependency.
+                    //
+                    // By using `in_task`, we're also creating an edge from the *current* query
+                    // to the newly-created `combined_dep_node`. This is probably redundant,
+                    // but it's better to add too many dep graph edges than to add too few
+                    // dep graph edges.
+                    let ((), combined_dep_node) = self.in_task(|this| {
+                        this.tcx().dep_graph.read_index(provisional_dep_node);
+                        this.tcx().dep_graph.read_index(dep_node);
+                    });
+                    self.insert_evaluation_cache(
+                        param_env,
+                        fresh_trait_pred,
+                        combined_dep_node,
+                        provisional_result.max(result),
+                    );
+                },
+            );
         } else {
             debug!(?result, "PROVISIONAL");
             debug!(
@@ -781,7 +805,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 fresh_trait_pred, stack.depth, reached_depth,
             );
 
-            stack.cache().insert_provisional(stack.dfn, reached_depth, fresh_trait_pred, result);
+            stack.cache().insert_provisional(
+                stack.dfn,
+                reached_depth,
+                fresh_trait_pred,
+                result,
+                dep_node,
+            );
         }
 
         Ok(result)
@@ -2506,6 +2536,11 @@ struct ProvisionalEvaluation {
     from_dfn: usize,
     reached_depth: usize,
     result: EvaluationResult,
+    /// The `DepNodeIndex` created for the `evaluate_stack` call for this provisional
+    /// evaluation. When we create an entry in the evaluation cache using this provisional
+    /// cache entry (see `on_completion`), we use this `dep_node` to ensure that future reads from
+    /// the cache will have all of the necessary incr comp dependencies tracked.
+    dep_node: DepNodeIndex,
 }
 
 impl<'tcx> Default for ProvisionalEvaluationCache<'tcx> {
@@ -2548,6 +2583,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
         reached_depth: usize,
         fresh_trait_pred: ty::PolyTraitPredicate<'tcx>,
         result: EvaluationResult,
+        dep_node: DepNodeIndex,
     ) {
         debug!(?from_dfn, ?fresh_trait_pred, ?result, "insert_provisional");
 
@@ -2573,7 +2609,10 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
             }
         }
 
-        map.insert(fresh_trait_pred, ProvisionalEvaluation { from_dfn, reached_depth, result });
+        map.insert(
+            fresh_trait_pred,
+            ProvisionalEvaluation { from_dfn, reached_depth, result, dep_node },
+        );
     }
 
     /// Invoked when the node with dfn `dfn` does not get a successful
@@ -2624,7 +2663,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
     fn on_completion(
         &self,
         dfn: usize,
-        mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult),
+        mut op: impl FnMut(ty::PolyTraitPredicate<'tcx>, EvaluationResult, DepNodeIndex),
     ) {
         debug!(?dfn, "on_completion");
 
@@ -2633,7 +2672,7 @@ impl<'tcx> ProvisionalEvaluationCache<'tcx> {
         {
             debug!(?fresh_trait_pred, ?eval, "on_completion");
 
-            op(fresh_trait_pred, eval.result);
+            op(fresh_trait_pred, eval.result, eval.dep_node);
         }
     }
 }
diff --git a/src/test/incremental/issue-92987-provisional-dep-node.rs b/src/test/incremental/issue-92987-provisional-dep-node.rs
new file mode 100644
index 00000000000..a48a8373c2b
--- /dev/null
+++ b/src/test/incremental/issue-92987-provisional-dep-node.rs
@@ -0,0 +1,24 @@
+// revisions: rpass1 rpass2
+
+// Regression test for issue #92987
+// Tests that we properly manage `DepNode`s during trait evaluation
+// involing an auto-trait cycle.
+
+#[cfg(rpass1)]
+struct CycleOne(Box<CycleTwo>);
+
+#[cfg(rpass2)]
+enum CycleOne {
+    Variant(Box<CycleTwo>)
+}
+
+struct CycleTwo(CycleOne);
+
+fn assert_send<T: Send>() {}
+
+fn bar() {
+    assert_send::<CycleOne>();
+    assert_send::<CycleTwo>();
+}
+
+fn main() {}