Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions compiler/rustc_type_ir/src/search_graph/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,8 +628,8 @@ pub struct SearchGraph<D: Delegate<Cx = X>, X: Cx = <D as Delegate>::Cx> {
/// don't need to track the nested goals used while computing a provisional
/// cache entry.
enum UpdateParentGoalCtxt<'a, X: Cx> {
Ordinary { nested_goals: &'a NestedGoals<X>, min_reachable_available_depth: AvailableDepth },
CycleOnStack(X::Input),
Ordinary { nested_goals: &'a NestedGoals<X> },
CycleOnStack { head: X::Input },
ProvisionalCacheHit,
}

Expand All @@ -653,9 +653,12 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
heads: impl Iterator<Item = (StackDepth, CycleHead)>,
encountered_overflow: bool,
context: UpdateParentGoalCtxt<'_, X>,
min_reachable_available_depth: AvailableDepth,
) {
if let Some((parent_index, parent)) = stack.last_mut_with_index() {
parent.encountered_overflow |= encountered_overflow;
parent.min_reached_available_depth =
parent.min_reached_available_depth.min(min_reachable_available_depth);

for (head_index, head) in heads {
if let Some(candidate_usages) = &mut parent.candidate_usages {
Expand All @@ -679,13 +682,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
}
}
let parent_depends_on_cycle = match context {
UpdateParentGoalCtxt::Ordinary { nested_goals, min_reachable_available_depth } => {
parent.min_reached_available_depth =
parent.min_reached_available_depth.min(min_reachable_available_depth);
UpdateParentGoalCtxt::Ordinary { nested_goals } => {
parent.nested_goals.extend_from_child(step_kind_from_parent, nested_goals);
!nested_goals.is_empty()
}
UpdateParentGoalCtxt::CycleOnStack(head) => {
UpdateParentGoalCtxt::CycleOnStack { head } => {
// We lookup provisional cache entries before detecting cycles.
// We therefore can't use a global cache entry if it contains a cycle
// whose head is in the provisional cache.
Expand Down Expand Up @@ -811,7 +812,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
// - A
// - BA cycle
// - CB :x:
if let Some(result) = self.lookup_provisional_cache(input, step_kind_from_parent) {
if let Some(result) =
self.lookup_provisional_cache(input, step_kind_from_parent, available_depth)
{
return result;
}

Expand Down Expand Up @@ -839,7 +842,9 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
// avoid iterating over the stack in case a goal has already been computed.
// This may not have an actual performance impact and we could reorder them
// as it may reduce the number of `nested_goals` we need to track.
if let Some(result) = self.check_cycle_on_stack(cx, input, step_kind_from_parent) {
if let Some(result) =
self.check_cycle_on_stack(cx, input, step_kind_from_parent, available_depth)
{
debug_assert!(validate_cache.is_none(), "global cache and cycle on stack: {input:?}");
return result;
}
Expand Down Expand Up @@ -874,10 +879,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
step_kind_from_parent,
evaluation_result.heads.iter(),
evaluation_result.encountered_overflow,
UpdateParentGoalCtxt::Ordinary {
nested_goals: &evaluation_result.nested_goals,
min_reachable_available_depth: available_depth - evaluation_result.required_depth,
},
UpdateParentGoalCtxt::Ordinary { nested_goals: &evaluation_result.nested_goals },
available_depth - evaluation_result.required_depth,
);
let result = evaluation_result.result;

Expand Down Expand Up @@ -1119,6 +1122,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
&mut self,
input: X::Input,
step_kind_from_parent: PathKind,
available_depth: AvailableDepth,
) -> Option<X::Result> {
if !D::ENABLE_PROVISIONAL_CACHE {
return None;
Expand Down Expand Up @@ -1157,6 +1161,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
heads.iter(),
encountered_overflow,
UpdateParentGoalCtxt::ProvisionalCacheHit,
available_depth,
);
debug!(?head_index, ?path_from_head, "provisional cache hit");
return Some(result);
Expand Down Expand Up @@ -1278,10 +1283,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
step_kind_from_parent,
heads,
encountered_overflow,
UpdateParentGoalCtxt::Ordinary {
nested_goals,
min_reachable_available_depth: available_depth - required_depth,
},
UpdateParentGoalCtxt::Ordinary { nested_goals },
available_depth - required_depth,
);

debug!(?required_depth, "global cache hit");
Expand All @@ -1294,6 +1297,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
cx: X,
input: X::Input,
step_kind_from_parent: PathKind,
available_depth: AvailableDepth,
) -> Option<X::Result> {
let head_index = self.stack.find(input)?;
// We have a nested goal which directly relies on a goal deeper in the stack.
Expand All @@ -1312,7 +1316,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D, X> {
step_kind_from_parent,
iter::once((head_index, head)),
false,
UpdateParentGoalCtxt::CycleOnStack(input),
UpdateParentGoalCtxt::CycleOnStack { head: input },
available_depth,
);

// Return the provisional result or, if we're in the first iteration,
Expand Down
27 changes: 27 additions & 0 deletions tests/ui/traits/next-solver/overflow/cycle-required-depth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ compile-flags: -Znext-solver
//@ check-pass

// We must properly decrement available_depth when encountering cycles.
// Proving `Foo: Send` results in a cycle:
// - Foo: Send
// ->PhantomData<Foo>: Send
// ->Foo: Send
// and if we cache this result with too small of a required depth, the second
// `require` call will succeed with the cached result where it would fail if
// the goal were freshly evaluated.

#![recursion_limit = "6"]

use std::marker::PhantomData;

pub struct Foo(PhantomData<Foo>);
pub struct W<T>(T);

fn require<T: Send>() {}

fn main() {
require::<Foo>();
require::<W<W<W<W<W<Foo>>>>>>();
//~^ WARN overflow evaluating the requirement `W<W<W<W<W<Foo>>>>>: Send`
//~| WARN this was previously accepted by the compiler but is being phased out
}
21 changes: 21 additions & 0 deletions tests/ui/traits/next-solver/overflow/cycle-required-depth.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: overflow evaluating the requirement `W<W<W<W<W<Foo>>>>>: Send`
--> $DIR/cycle-required-depth.rs:24:5
|
LL | require::<W<W<W<W<W<Foo>>>>>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: which requires `W<W<W<W<Foo>>>>: Send`
= note: which requires `W<W<W<Foo>>>: Send`
= note: which requires `W<W<Foo>>: Send`
= note: which requires `W<Foo>: Send`
= note: which requires `Foo: Send`
= note: and so on...
= help: consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved
= help: or consider increasing the recursion limit by adding a `#![recursion_limit = "12"]` attribute to your crate (`cycle_required_depth`)
= note: this lint is attached to the whole crate and can't be disabled on a per-function basis
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #159228 <https://github.com/rust-lang/rust/issues/159228>
= note: `#[warn(recursion_depth_exceeding_limit)]` (part of `#[warn(future_incompatible)]`) on by default

warning: 1 warning emitted

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//@ compile-flags: -Znext-solver
//@ check-pass

#![recursion_limit = "6"]
use std::marker::PhantomData;

// We need to decrement available_depth on provisional cache hits.
// e.g., in the following example:
// A: Send, avail = 6
// -> PhantomData<A>: Send, avail = 5
// -> A: Send, avail = 4
// -> B: Send, avail = 5
// -> C: Send, avail = 4
// -> PhantomData<A>: Send, provisional cache hit, avail = 4 if we don't decrement
struct A(PhantomData<A>, B);
struct B(C);
struct C(PhantomData<A>);
struct W<T>(T);

fn require<T: Send>() {}

fn main() {
require::<A>();
require::<W<W<W<W<A>>>>>();
//~^ WARN overflow evaluating the requirement `W<W<W<W<A>>>>: Send`
//~| WARN this was previously accepted by the compiler but is being phased out
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: overflow evaluating the requirement `W<W<W<W<A>>>>: Send`
--> $DIR/provisional-cache-required-depth.rs:24:5
|
LL | require::<W<W<W<W<A>>>>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: which requires `W<W<W<A>>>: Send`
= note: which requires `W<W<A>>: Send`
= note: which requires `W<A>: Send`
= note: which requires `A: Send`
= note: which requires `B: Send`
= note: and so on...
= help: consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved
= help: or consider increasing the recursion limit by adding a `#![recursion_limit = "12"]` attribute to your crate (`provisional_cache_required_depth`)
= note: this lint is attached to the whole crate and can't be disabled on a per-function basis
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #159228 <https://github.com/rust-lang/rust/issues/159228>
= note: `#[warn(recursion_depth_exceeding_limit)]` (part of `#[warn(future_incompatible)]`) on by default

warning: 1 warning emitted

Loading