Skip to content

Commit 25621e4

Browse files
committed
change definitely non-productive cycles to error
1 parent db5624f commit 25621e4

21 files changed

+288
-275
lines changed

Diff for: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+1-1
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)),

Diff for: compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,31 @@ where
262262
self.delegate.typing_mode()
263263
}
264264

265+
#[instrument(level = "trace", skip(self), ret)]
265266
pub(super) fn step_kind_for_source(&self, source: GoalSource) -> PathKind {
266-
match (self.current_goal_kind, source) {
267-
(
268-
CurrentGoalKind::CoinductiveTrait,
269-
GoalSource::ImplWhereBound | GoalSource::NormalizeImplWhereBound,
270-
) => PathKind::Coinductive,
271-
_ => PathKind::Inductive,
267+
match source {
268+
GoalSource::NormalizeGoal(path_kind) => path_kind,
269+
GoalSource::ImplWhereBound => {
270+
// We currently only consider a cycle coinductive if it steps
271+
// into a where-clause of a coinductive trait.
272+
//
273+
// We probably want to make all traits coinductive in the future,
274+
// so we treat cycles involving their where-clauses as ambiguous.
275+
if let CurrentGoalKind::CoinductiveTrait = self.current_goal_kind {
276+
PathKind::Coinductive
277+
} else {
278+
PathKind::Unknown
279+
}
280+
}
281+
// We treat checking super trait bounds for unknowable candidates as
282+
// unknown. Treating them is inductive would cause be unsound as it causes
283+
// tests/ui/traits/next-solver/coherence/ambiguity-causes-visitor-hang.rs
284+
// to compile.
285+
GoalSource::CoherenceUnknowableSuper => PathKind::Unknown,
286+
GoalSource::Misc
287+
| GoalSource::AliasBoundConstCondition
288+
| GoalSource::AliasWellFormed
289+
| GoalSource::InstantiateHigherRanked => PathKind::Inductive,
272290
}
273291
}
274292

@@ -1120,14 +1138,11 @@ where
11201138
for_goal_source: GoalSource,
11211139
param_env: I::ParamEnv,
11221140
) -> Self {
1123-
let normalization_goal_source = match for_goal_source {
1124-
GoalSource::ImplWhereBound => GoalSource::NormalizeImplWhereBound,
1125-
_ => GoalSource::Misc,
1126-
};
1141+
let step_kind = ecx.step_kind_for_source(for_goal_source);
11271142
ReplaceAliasWithInfer {
11281143
ecx,
11291144
param_env,
1130-
normalization_goal_source,
1145+
normalization_goal_source: GoalSource::NormalizeGoal(step_kind),
11311146
cache: Default::default(),
11321147
}
11331148
}

Diff for: compiler/rustc_next_trait_solver/src/solve/search_graph.rs

+4-8
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(

Diff for: compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,13 @@ 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(_),
443-
GoalSource::Misc | GoalSource::NormalizeImplWhereBound,
447+
GoalSource::Misc | GoalSource::NormalizeGoal(_),
444448
) => {
445449
continue;
446450
}

Diff for: compiler/rustc_type_ir/src/search_graph/mod.rs

+104-51
Original file line numberDiff line numberDiff line change
@@ -13,12 +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};
23+
#[cfg(feature = "nightly")]
24+
use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
2225
use tracing::debug;
2326

2427
use crate::data_structures::HashMap;
@@ -109,20 +112,29 @@ pub trait Delegate {
109112
/// In the initial iteration of a cycle, we do not yet have a provisional
110113
/// result. In the case we return an initial provisional result depending
111114
/// on the kind of cycle.
112-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116+
#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
113117
pub enum PathKind {
114-
Coinductive,
118+
/// A path consisting of only inductive/unproductive steps.
115119
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,
116126
}
127+
117128
impl PathKind {
118129
/// Returns the path kind when merging `self` with `rest`.
119130
///
120131
/// Given an inductive path `self` and a coinductive path `rest`,
121132
/// the path `self -> rest` would be coinductive.
122133
fn extend(self, rest: PathKind) -> PathKind {
123-
match self {
124-
PathKind::Coinductive => PathKind::Coinductive,
125-
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,
126138
}
127139
}
128140
}
@@ -156,9 +168,6 @@ impl UsageKind {
156168
}
157169
}
158170
}
159-
fn and_merge(&mut self, other: impl Into<Self>) {
160-
*self = self.merge(other);
161-
}
162171
}
163172

164173
/// For each goal we track whether the paths from this goal
@@ -294,14 +303,68 @@ impl CycleHeads {
294303

295304
let path_from_entry = match step_kind {
296305
PathKind::Coinductive => AllPathsToHeadCoinductive::Yes,
297-
PathKind::Inductive => path_from_entry,
306+
PathKind::Unknown | PathKind::Inductive => path_from_entry,
298307
};
299308

300309
self.insert(head, path_from_entry);
301310
}
302311
}
303312
}
304313

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+
305368
/// The nested goals of each stack entry and the path from the
306369
/// stack entry to that nested goal.
307370
///
@@ -319,15 +382,18 @@ impl CycleHeads {
319382
/// results from a the cycle BAB depending on the cycle root.
320383
#[derive_where(Debug, Default, Clone; X: Cx)]
321384
struct NestedGoals<X: Cx> {
322-
nested_goals: HashMap<X::Input, UsageKind>,
385+
nested_goals: HashMap<X::Input, PathsToNested>,
323386
}
324387
impl<X: Cx> NestedGoals<X> {
325388
fn is_empty(&self) -> bool {
326389
self.nested_goals.is_empty()
327390
}
328391

329-
fn insert(&mut self, input: X::Input, path_from_entry: UsageKind) {
330-
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+
}
331397
}
332398

333399
/// Adds the nested goals of a nested goal, given that the path `step_kind` from this goal
@@ -338,18 +404,15 @@ impl<X: Cx> NestedGoals<X> {
338404
/// the same as for the child.
339405
fn extend_from_child(&mut self, step_kind: PathKind, nested_goals: &NestedGoals<X>) {
340406
#[allow(rustc::potential_query_instability)]
341-
for (input, path_from_entry) in nested_goals.iter() {
342-
let path_from_entry = match step_kind {
343-
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
344-
PathKind::Inductive => path_from_entry,
345-
};
346-
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);
347410
}
348411
}
349412

350413
#[cfg_attr(feature = "nightly", rustc_lint_query_instability)]
351414
#[allow(rustc::potential_query_instability)]
352-
fn iter(&self) -> impl Iterator<Item = (X::Input, UsageKind)> + '_ {
415+
fn iter(&self) -> impl Iterator<Item = (X::Input, PathsToNested)> + '_ {
353416
self.nested_goals.iter().map(|(i, p)| (*i, *p))
354417
}
355418

@@ -487,7 +550,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
487550
// goals as this change may cause them to now depend on additional
488551
// goals, resulting in new cycles. See the dev-guide for examples.
489552
if parent_depends_on_cycle {
490-
parent.nested_goals.insert(parent.input, UsageKind::Single(PathKind::Inductive))
553+
parent.nested_goals.insert(parent.input, PathsToNested::EMPTY);
491554
}
492555
}
493556
}
@@ -663,7 +726,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
663726
//
664727
// We must therefore not use the global cache entry for `B` in that case.
665728
// See tests/ui/traits/next-solver/cycles/hidden-by-overflow.rs
666-
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
729+
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
667730
}
668731

669732
debug!("encountered stack overflow");
@@ -745,16 +808,11 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
745808

746809
// We now care about the path from the next highest cycle head to the
747810
// provisional cache entry.
748-
match path_from_head {
749-
PathKind::Coinductive => {}
750-
PathKind::Inductive => {
751-
*path_from_head = Self::cycle_path_kind(
752-
&self.stack,
753-
stack_entry.step_kind_from_parent,
754-
head,
755-
)
756-
}
757-
}
811+
*path_from_head = path_from_head.extend(Self::cycle_path_kind(
812+
&self.stack,
813+
stack_entry.step_kind_from_parent,
814+
head,
815+
));
758816
// Mutate the result of the provisional cache entry in case we did
759817
// not reach a fixpoint.
760818
*result = mutate_result(input, *result);
@@ -854,7 +912,7 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
854912
for &ProvisionalCacheEntry {
855913
encountered_overflow,
856914
ref heads,
857-
path_from_head,
915+
path_from_head: head_to_provisional,
858916
result: _,
859917
} in entries.iter()
860918
{
@@ -866,24 +924,19 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
866924

867925
// A provisional cache entry only applies if the path from its highest head
868926
// matches the path when encountering the goal.
927+
//
928+
// We check if any of the paths taken while computing the global goal
929+
// would end up with an applicable provisional cache entry.
869930
let head = heads.highest_cycle_head();
870-
let full_path = match Self::cycle_path_kind(stack, step_kind_from_parent, head) {
871-
PathKind::Coinductive => UsageKind::Single(PathKind::Coinductive),
872-
PathKind::Inductive => path_from_global_entry,
873-
};
874-
875-
match (full_path, path_from_head) {
876-
(UsageKind::Mixed, _)
877-
| (UsageKind::Single(PathKind::Coinductive), PathKind::Coinductive)
878-
| (UsageKind::Single(PathKind::Inductive), PathKind::Inductive) => {
879-
debug!(
880-
?full_path,
881-
?path_from_head,
882-
"cache entry not applicable due to matching paths"
883-
);
884-
return false;
885-
}
886-
_ => debug!(?full_path, ?path_from_head, "paths don't match"),
931+
let head_to_curr = Self::cycle_path_kind(stack, step_kind_from_parent, head);
932+
let full_paths = path_from_global_entry.extend_with(head_to_curr);
933+
if full_paths.contains(head_to_provisional.into()) {
934+
debug!(
935+
?full_paths,
936+
?head_to_provisional,
937+
"cache entry not applicable due to matching paths"
938+
);
939+
return false;
887940
}
888941
}
889942
}
@@ -982,8 +1035,8 @@ impl<D: Delegate<Cx = X>, X: Cx> SearchGraph<D> {
9821035
let last = &mut self.stack[last_index];
9831036
last.reached_depth = last.reached_depth.max(next_index);
9841037

985-
last.nested_goals.insert(input, UsageKind::Single(step_kind_from_parent));
986-
last.nested_goals.insert(last.input, UsageKind::Single(PathKind::Inductive));
1038+
last.nested_goals.insert(input, step_kind_from_parent.into());
1039+
last.nested_goals.insert(last.input, PathsToNested::EMPTY);
9871040
if last_index != head {
9881041
last.heads.insert(head, step_kind_from_parent);
9891042
}

0 commit comments

Comments
 (0)