Skip to content

Commit 1b40169

Browse files
committed
change definitely non-productive cycles to error
1 parent c44ecdf commit 1b40169

File tree

21 files changed

+275
-266
lines changed

21 files changed

+275
-266
lines changed

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -740,7 +740,7 @@ where
740740
// We skip the goal itself as that one would cycle.
741741
let predicate: I::Predicate = trait_ref.upcast(cx);
742742
ecx.add_goals(
743-
GoalSource::Misc,
743+
GoalSource::CoherenceUnknowableSuper,
744744
elaborate::elaborate(cx, [predicate])
745745
.skip(1)
746746
.map(|predicate| goal.with(cx, predicate)),

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -271,12 +271,29 @@ where
271271
/// and will need to clearly document it in the rustc-dev-guide before
272272
/// stabilization.
273273
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
274-
match (self.current_goal_kind, source) {
275-
(_, GoalSource::NormalizeGoal(step_kind)) => step_kind,
276-
(CurrentGoalKind::CoinductiveTrait, GoalSource::ImplWhereBound) => {
277-
PathKind::Coinductive
274+
match source {
275+
GoalSource::NormalizeGoal(path_kind) => path_kind,
276+
GoalSource::ImplWhereBound => {
277+
// We currently only consider a cycle coinductive if it steps
278+
// into a where-clause of a coinductive trait.
279+
//
280+
// We probably want to make all traits coinductive in the future,
281+
// so we treat cycles involving their where-clauses as ambiguous.
282+
if let CurrentGoalKind::CoinductiveTrait = self.current_goal_kind {
283+
PathKind::Coinductive
284+
} else {
285+
PathKind::Unknown
286+
}
278287
}
279-
_ => PathKind::Inductive,
288+
// We treat checking super trait bounds for unknowable candidates as
289+
// unknown. Treating them is inductive would cause be unsound as it causes
290+
// tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.rs
291+
// to compile.
292+
GoalSource::CoherenceUnknowableSuper => PathKind::Unknown,
293+
GoalSource::Misc
294+
| GoalSource::AliasBoundConstCondition
295+
| GoalSource::AliasWellFormed
296+
| GoalSource::InstantiateHigherRanked => PathKind::Inductive,
280297
}
281298
}
282299

compiler/rustc_next_trait_solver/src/solve/search_graph.rs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::marker::PhantomData;
33

44
use rustc_type_ir::Interner;
55
use rustc_type_ir::search_graph::{self, PathKind};
6-
use rustc_type_ir::solve::{CanonicalInput, Certainty, QueryResult};
6+
use rustc_type_ir::solve::{CanonicalInput, Certainty, NoSolution, QueryResult};
77

88
use super::inspect::ProofTreeBuilder;
99
use super::{FIXPOINT_STEP_LIMIT, has_no_inference_or_external_constraints};
@@ -47,7 +47,8 @@ where
4747
) -> QueryResult<I> {
4848
match kind {
4949
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes),
50-
PathKind::Inductive => response_no_constraints(cx, input, Certainty::overflow(false)),
50+
PathKind::Unknown => response_no_constraints(cx, input, Certainty::overflow(false)),
51+
PathKind::Inductive => Err(NoSolution),
5152
}
5253
}
5354

@@ -57,12 +58,7 @@ where
5758
input: CanonicalInput<I>,
5859
result: QueryResult<I>,
5960
) -> bool {
60-
match kind {
61-
PathKind::Coinductive => response_no_constraints(cx, input, Certainty::Yes) == result,
62-
PathKind::Inductive => {
63-
response_no_constraints(cx, input, Certainty::overflow(false)) == result
64-
}
65-
}
61+
Self::initial_provisional_result(cx, kind, input) == result
6662
}
6763

6864
fn on_stack_overflow(

compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,10 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
438438

439439
let obligation;
440440
match (child_mode, nested_goal.source()) {
441+
// We shouldn't really care about coherence unknowable candidates
442+
// as we never report fulfillment errors involving them. However, this
443+
// is still reachable when building the fulfillment errors.
444+
(_, GoalSource::CoherenceUnknowableSuper) => continue,
441445
(
442446
ChildMode::Trait(_) | ChildMode::Host(_),
443447
GoalSource::Misc | GoalSource::NormalizeGoal(_),

compiler/rustc_type_ir/src/search_graph/mod.rs

Lines changed: 103 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@
1313
/// behavior as long as the resulting behavior is still correct.
1414
use std::cmp::Ordering;
1515
use std::collections::BTreeMap;
16+
use std::collections::hash_map::Entry;
1617
use std::fmt::Debug;
1718
use std::hash::Hash;
1819
use std::marker::PhantomData;
1920

2021
use derive_where::derive_where;
2122
use rustc_index::{Idx, IndexVec};
2223
#[cfg(feature = "nightly")]
23-
use rustc_macros::HashStable_NoContext;
24+
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
2425
use tracing::debug;
2526

2627
use crate::data_structures::HashMap;
@@ -111,21 +112,29 @@ pub trait Delegate {
111112
/// In the initial iteration of a cycle, we do not yet have a provisional
112113
/// result. In the case we return an initial provisional result depending
113114
/// on the kind of cycle.
114-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
115-
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
115+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116+
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
116117
pub enum PathKind {
117-
Coinductive,
118+
/// A path consisting of only inductive/unproductive steps.
118119
Inductive,
120+
/// A path which is not be coinductive right now but we may want
121+
/// to change of them to be so in the future. We return an ambiguous
122+
/// result in this case to prevent people from relying on this.
123+
Unknown,
124+
/// A path with at least one coinductive step. Such cycles hold.
125+
Coinductive,
119126
}
127+
120128
impl PathKind {
121129
/// Returns the path kind when merging `self` with `rest`.
122130
///
123131
/// Given an inductive path `self` and a coinductive path `rest`,
124132
/// the path `self -> rest` would be coinductive.
125133
fn extend(self, rest: PathKind) -> PathKind {
126-
match self {
127-
PathKind::Coinductive => PathKind::Coinductive,
128-
PathKind::Inductive => rest,
134+
match (self, rest) {
135+
(PathKind::Coinductive, _) | (_, PathKind::Coinductive) => PathKind::Coinductive,
136+
(PathKind::Unknown, _) | (_, PathKind::Unknown) => PathKind::Unknown,
137+
(PathKind::Inductive, PathKind::Inductive) => PathKind::Inductive,
129138
}
130139
}
131140
}
@@ -159,9 +168,6 @@ impl UsageKind {
159168
}
160169
}
161170
}
162-
fn and_merge(&mut self, other: impl Into<Self>) {
163-
*self = self.merge(other);
164-
}
165171
}
166172

167173
/// For each goal we track whether the paths from this goal
@@ -297,14 +303,68 @@ impl CycleHeads {
297303

298304
let path_from_entry = match step_kind {
299305
PathKind::Coinductive => AllPathsToHeadCoinductive::Yes,
300-
PathKind::Inductive => path_from_entry,
306+
PathKind::Unknown | PathKind::Inductive => path_from_entry,
301307
};
302308

303309
self.insert(head, path_from_entry);
304310
}
305311
}
306312
}
307313

314+
bitflags::bitflags! {
315+
/// Tracks how nested goals have been accessed. This is necessary to disable
316+
/// global cache entries if computing them would otherwise result in a cycle or
317+
/// access a provisional cache entry.
318+
#[derive(Debug, Clone, Copy)]
319+
pub struct PathsToNested: u8 {
320+
/// The initial value when adding a goal to its own nested goals.
321+
const EMPTY = 1 << 0;
322+
const INDUCTIVE = 1 << 1;
323+
const UNKNOWN = 1 << 2;
324+
const COINDUCTIVE = 1 << 3;
325+
}
326+
}
327+
impl From<PathKind> for PathsToNested {
328+
fn from(path: PathKind) -> PathsToNested {
329+
match path {
330+
PathKind::Inductive => PathsToNested::INDUCTIVE,
331+
PathKind::Unknown => PathsToNested::UNKNOWN,
332+
PathKind::Coinductive => PathsToNested::COINDUCTIVE,
333+
}
334+
}
335+
}
336+
impl PathsToNested {
337+
#[must_use]
338+
fn extend_with(mut self, path: PathKind) -> Self {
339+
match path {
340+
PathKind::Inductive => {
341+
if self.intersects(PathsToNested::EMPTY) {
342+
self.remove(PathsToNested::EMPTY);
343+
self.insert(PathsToNested::INDUCTIVE);
344+
}
345+
}
346+
PathKind::Unknown => {
347+
if self.intersects(PathsToNested::EMPTY | PathsToNested::INDUCTIVE) {
348+
self.remove(PathsToNested::EMPTY | PathsToNested::INDUCTIVE);
349+
self.insert(PathsToNested::UNKNOWN);
350+
}
351+
}
352+
PathKind::Coinductive => {
353+
if self.intersects(
354+
PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
355+
) {
356+
self.remove(
357+
PathsToNested::EMPTY | PathsToNested::INDUCTIVE | PathsToNested::UNKNOWN,
358+
);
359+
self.insert(PathsToNested::COINDUCTIVE);
360+
}
361+
}
362+
}
363+
364+
self
365+
}
366+
}
367+
308368
/// The nested goals of each stack entry and the path from the
309369
/// stack entry to that nested goal.
310370
///
@@ -322,15 +382,18 @@ impl CycleHeads {
322382
/// results from a the cycle BAB depending on the cycle root.
323383
#[derive_where(Debug, Default, Clone; X: Cx)]
324384
struct NestedGoals<X: Cx> {
325-
nested_goals: HashMap<X::Input, UsageKind>,
385+
nested_goals: HashMap<X::Input, PathsToNested>,
326386
}
327387
impl<X: Cx> NestedGoals<X> {
328388
fn is_empty(&self) -> bool {
329389
self.nested_goals.is_empty()
330390
}
331391

332-
fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) {
333-
self.nested_goals.entry(input).or_insert(path_from_entry).and_merge(path_from_entry);
392+
fn insert(&mut self, input: X::Input, paths_to_nested: PathsToNested) {
393+
match self.nested_goals.entry(input) {
394+
Entry::Occupied(mut entry) => *entry.get_mut() |= paths_to_nested,
395+
Entry::Vacant(entry) => drop(entry.insert(paths_to_nested)),
396+
}
334397
}
335398

336399
/// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
@@ -341,18 +404,15 @@ impl<X: Cx> NestedGoals<X> {
341404
/// the same as for the child.
342405
fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
343406
#[allow(rustc::potential_query_instability)]
344-
for (input, path_from_entry) in nested_goals.iter() {
345-
let path_from_entry = match step_kind {
346-
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
347-
PathKind::Inductive => path_from_entry,
348-
};
349-
self.insert(input, path_from_entry);
407+
for (input, paths_to_nested) in nested_goals.iter() {
408+
let paths_to_nested = paths_to_nested.extend_with(step_kind);
409+
self.insert(input, paths_to_nested);
350410
}
351411
}
352412

353413
#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
354414
#[allow(rustc::potential_query_instability)]
355-
fn iter(&self) -> impl Iterator<Item = (X::Input, UsageKind)> {
415+
fn iter(&self) -> impl Iterator<Item = (X::Input, PathsToNested)> + '_ {
356416
self.nested_goals.iter().map(|(i, p)| (*i, *p))
357417
}
358418

@@ -490,7 +550,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
490550
// goals as this change may cause them to now depend on additional
491551
// goals, resulting in new cycles. See the dev-guide for examples.
492552
if parent_depends_on_cycle {
493-
parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Inductive))
553+
parent.nested_goals.insert(parent.input, PathsToNested::EMPTY);
494554
}
495555
}
496556
}
@@ -666,7 +726,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
666726
//
667727
// We must therefore not use the global cache entry for `B` in that case.
668728
// See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
669-
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
729+
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
670730
}
671731

672732
debug!("encountered stack overflow");
@@ -749,16 +809,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
749809

750810
// We now care about the path from the next highest cycle head to the
751811
// provisional cache entry.
752-
match path_from_head {
753-
PathKind::Coinductive => {}
754-
PathKind::Inductive => {
755-
*path_from_head = Self::cycle_path_kind(
756-
&self.stack,
757-
stack_entry.step_kind_from_parent,
758-
head,
759-
)
760-
}
761-
}
812+
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
813+
&self.stack,
814+
stack_entry.step_kind_from_parent,
815+
head,
816+
));
762817
// Mutate the result of the provisional cache entry in case we did
763818
// not reach a fixpoint.
764819
*result = mutate_result(input, *result);
@@ -858,7 +913,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
858913
for &ProvisionalCacheEntry {
859914
encountered_overflow,
860915
ref heads,
861-
path_from_head,
916+
path_from_head: head_to_provisional,
862917
result: _,
863918
} in entries.iter()
864919
{
@@ -870,24 +925,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
870925

871926
// A provisional cache entry only applies if the path from its highest head
872927
// matches the path when encountering the goal.
928+
//
929+
// We check if any of the paths taken while computing the global goal
930+
// would end up with an applicable provisional cache entry.
873931
let head = heads.highest_cycle_head();
874-
let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) {
875-
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
876-
PathKind::Inductive => path_from_global_entry,
877-
};
878-
879-
match (full_path, path_from_head) {
880-
(UsageKind::Mixed, _)
881-
| (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive)
882-
| (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => {
883-
debug!(
884-
?full_path,
885-
?path_from_head,
886-
"cache entry not applicable due to matching paths"
887-
);
888-
return false;
889-
}
890-
_ => debug!(?full_path, ?path_from_head, "paths don't match"),
932+
let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head);
933+
let full_paths = path_from_global_entry.extend_with(head_to_curr);
934+
if full_paths.contains(head_to_provisional.into()) {
935+
debug!(
936+
?full_paths,
937+
?head_to_provisional,
938+
"cache entry not applicable due to matching paths"
939+
);
940+
return false;
891941
}
892942
}
893943
}
@@ -986,8 +1036,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
9861036
let last = &mut self.stack[last_index];
9871037
last.reached_depth = last.reached_depth.max(next_index);
9881038

989-
last.nested_goals.insert(input, UsageKind::Single(step_kind_from_parent));
990-
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
1039+
last.nested_goals.insert(input, step_kind_from_parent.into());
1040+
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
9911041
if last_index != head {
9921042
last.heads.insert(head, step_kind_from_parent);
9931043
}

compiler/rustc_type_ir/src/solve/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ pub enum GoalSource {
7979
/// This is used in two places: projecting to an opaque whose hidden type
8080
/// is already registered in the opaque type storage, and for rigid projections.
8181
AliasWellFormed,
82-
82+
/// Proving super trait bounds while checking whether a trait or projection
83+
/// goal is unknowable.
84+
///
85+
/// We must not treat these as an inductive steps, see `EvalCtxt::step_kind_for_source`.
86+
CoherenceUnknowableSuper,
8387
/// In case normalizing aliases in nested goals cycles, eagerly normalizing these
8488
/// aliases in the context of the parent may incorrectly change the cycle kind.
8589
/// Normalizing aliases in goals therefore tracks the original path kind for this

tests/ui/associated-type-bounds/hrtb.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
//@ check-pass
2+
//@ revisions: current next
3+
//@[next] compile-flags: -Znext-solver
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
25

36
trait A<'a> {}
47
trait B<'b> {}

tests/ui/associated-type-bounds/supertrait-defines-ty.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
//@ check-pass
2+
//@ revisions: current next
3+
//@[next] compile-flags: -Znext-solver
4+
//@ ignore-compare-mode-next-solver (explicit revisions)
25

36
// Make sure that we don't look into associated type bounds when looking for
47
// supertraits that define an associated type. Fixes #76593.

0 commit comments

Comments
 (0)