Skip to content

Commit e4e4eec

Browse files
authored
Rollup merge of rust-lang#102037 - jyn514:normalize-docs, r=lcnr
Make cycle errors recoverable In particular, this allows rustdoc to recover from cycle errors when normalizing associated types for documentation. In the past, `@jackh726` has said we need to be careful about overflow errors: rust-lang#91430 (comment) > Off the top of my head, we definitely should be careful about treating overflow errors the same as "not implemented for some reason" errors. Otherwise, you could end up with behavior that is different depending on recursion depth. But, that might be context-dependent. But cycle errors should be safe to unconditionally report; they don't depend on the recursion depth, they will always be an error whenever they're encountered. Helps with rust-lang#81091. r? `@lcnr` cc `@matthewjasper`
2 parents 656f08d + 1512ce5 commit e4e4eec

File tree

9 files changed

+58
-30
lines changed

9 files changed

+58
-30
lines changed

compiler/rustc_data_structures/src/obligation_forest/mod.rs

+26-11
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ pub trait ForestObligation: Clone + Debug {
9595
pub trait ObligationProcessor {
9696
type Obligation: ForestObligation;
9797
type Error: Debug;
98+
type OUT: OutcomeTrait<
99+
Obligation = Self::Obligation,
100+
Error = Error<Self::Obligation, Self::Error>,
101+
>;
98102

99103
fn needs_process_obligation(&self, obligation: &Self::Obligation) -> bool;
100104

@@ -111,7 +115,11 @@ pub trait ObligationProcessor {
111115
/// In other words, if we had O1 which required O2 which required
112116
/// O3 which required O1, we would give an iterator yielding O1,
113117
/// O2, O3 (O1 is not yielded twice).
114-
fn process_backedge<'c, I>(&mut self, cycle: I, _marker: PhantomData<&'c Self::Obligation>)
118+
fn process_backedge<'c, I>(
119+
&mut self,
120+
cycle: I,
121+
_marker: PhantomData<&'c Self::Obligation>,
122+
) -> Result<(), Self::Error>
115123
where
116124
I: Clone + Iterator<Item = &'c Self::Obligation>;
117125
}
@@ -402,12 +410,11 @@ impl<O: ForestObligation> ObligationForest<O> {
402410

403411
/// Performs a fixpoint computation over the obligation list.
404412
#[inline(never)]
405-
pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
413+
pub fn process_obligations<P>(&mut self, processor: &mut P) -> P::OUT
406414
where
407415
P: ObligationProcessor<Obligation = O>,
408-
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
409416
{
410-
let mut outcome = OUT::new();
417+
let mut outcome = P::OUT::new();
411418

412419
// Fixpoint computation: we repeat until the inner loop stalls.
413420
loop {
@@ -473,7 +480,7 @@ impl<O: ForestObligation> ObligationForest<O> {
473480
}
474481

475482
self.mark_successes();
476-
self.process_cycles(processor);
483+
self.process_cycles(processor, &mut outcome);
477484
self.compress(|obl| outcome.record_completed(obl));
478485
}
479486

@@ -558,7 +565,7 @@ impl<O: ForestObligation> ObligationForest<O> {
558565

559566
/// Report cycles between all `Success` nodes, and convert all `Success`
560567
/// nodes to `Done`. This must be called after `mark_successes`.
561-
fn process_cycles<P>(&mut self, processor: &mut P)
568+
fn process_cycles<P>(&mut self, processor: &mut P, outcome: &mut P::OUT)
562569
where
563570
P: ObligationProcessor<Obligation = O>,
564571
{
@@ -568,16 +575,21 @@ impl<O: ForestObligation> ObligationForest<O> {
568575
// to handle the no-op cases immediately to avoid the cost of the
569576
// function call.
570577
if node.state.get() == NodeState::Success {
571-
self.find_cycles_from_node(&mut stack, processor, index);
578+
self.find_cycles_from_node(&mut stack, processor, index, outcome);
572579
}
573580
}
574581

575582
debug_assert!(stack.is_empty());
576583
self.reused_node_vec = stack;
577584
}
578585

579-
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
580-
where
586+
fn find_cycles_from_node<P>(
587+
&self,
588+
stack: &mut Vec<usize>,
589+
processor: &mut P,
590+
index: usize,
591+
outcome: &mut P::OUT,
592+
) where
581593
P: ObligationProcessor<Obligation = O>,
582594
{
583595
let node = &self.nodes[index];
@@ -586,17 +598,20 @@ impl<O: ForestObligation> ObligationForest<O> {
586598
None => {
587599
stack.push(index);
588600
for &dep_index in node.dependents.iter() {
589-
self.find_cycles_from_node(stack, processor, dep_index);
601+
self.find_cycles_from_node(stack, processor, dep_index, outcome);
590602
}
591603
stack.pop();
592604
node.state.set(NodeState::Done);
593605
}
594606
Some(rpos) => {
595607
// Cycle detected.
596-
processor.process_backedge(
608+
let result = processor.process_backedge(
597609
stack[rpos..].iter().map(|&i| &self.nodes[i].obligation),
598610
PhantomData,
599611
);
612+
if let Err(err) = result {
613+
outcome.record_error(Error { error: err, backtrace: self.error_at(index) });
614+
}
600615
}
601616
}
602617
}

compiler/rustc_data_structures/src/obligation_forest/tests.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ where
6464
{
6565
type Obligation = O;
6666
type Error = E;
67+
type OUT = TestOutcome<O, E>;
6768

6869
fn needs_process_obligation(&self, _obligation: &Self::Obligation) -> bool {
6970
true
@@ -76,10 +77,15 @@ where
7677
(self.process_obligation)(obligation)
7778
}
7879

79-
fn process_backedge<'c, I>(&mut self, _cycle: I, _marker: PhantomData<&'c Self::Obligation>)
80+
fn process_backedge<'c, I>(
81+
&mut self,
82+
_cycle: I,
83+
_marker: PhantomData<&'c Self::Obligation>,
84+
) -> Result<(), Self::Error>
8085
where
8186
I: Clone + Iterator<Item = &'c Self::Obligation>,
8287
{
88+
Ok(())
8389
}
8490
}
8591

compiler/rustc_infer/src/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ pub struct FulfillmentError<'tcx> {
105105

106106
#[derive(Clone)]
107107
pub enum FulfillmentErrorCode<'tcx> {
108+
/// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented.
109+
CodeCycle(Vec<Obligation<'tcx, ty::Predicate<'tcx>>>),
108110
CodeSelectionError(SelectionError<'tcx>),
109111
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
110112
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate

compiler/rustc_infer/src/traits/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
4747
write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
4848
}
4949
super::CodeAmbiguity => write!(f, "Ambiguity"),
50+
super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle),
5051
}
5152
}
5253
}

compiler/rustc_trait_selection/src/traits/codegen.rs

+10
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
// general routines.
55

66
use crate::infer::{DefiningAnchor, TyCtxtInferExt};
7+
use crate::traits::error_reporting::InferCtxtExt;
78
use crate::traits::{
89
ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt,
910
Unimplemented,
1011
};
12+
use rustc_infer::traits::FulfillmentErrorCode;
1113
use rustc_middle::traits::CodegenObligationError;
1214
use rustc_middle::ty::{self, TyCtxt};
1315

@@ -62,6 +64,14 @@ pub fn codegen_select_candidate<'tcx>(
6264
// optimization to stop iterating early.
6365
let errors = fulfill_cx.select_all_or_error(&infcx);
6466
if !errors.is_empty() {
67+
// `rustc_monomorphize::collector` assumes there are no type errors.
68+
// Cycle errors are the only post-monomorphization errors possible; emit them now so
69+
// `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
70+
for err in errors {
71+
if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
72+
infcx.report_overflow_error_cycle(&cycle);
73+
}
74+
}
6575
return Err(CodegenObligationError::FulfillmentError);
6676
}
6777

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1540,6 +1540,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
15401540
}
15411541
diag.emit();
15421542
}
1543+
FulfillmentErrorCode::CodeCycle(ref cycle) => {
1544+
self.report_overflow_error_cycle(cycle);
1545+
}
15431546
}
15441547
}
15451548

compiler/rustc_trait_selection/src/traits/fulfill.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,9 @@ use super::Unimplemented;
2525
use super::{FulfillmentError, FulfillmentErrorCode};
2626
use super::{ObligationCause, PredicateObligation};
2727

28-
use crate::traits::error_reporting::InferCtxtExt as _;
2928
use crate::traits::project::PolyProjectionObligation;
3029
use crate::traits::project::ProjectionCacheKeyExt as _;
31-
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
30+
use crate::traits::query::evaluate_obligation::InferCtxtExt;
3231

3332
impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
3433
/// Note that we include both the `ParamEnv` and the `Predicate`,
@@ -224,6 +223,7 @@ fn mk_pending(os: Vec<PredicateObligation<'_>>) -> Vec<PendingPredicateObligatio
224223
impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
225224
type Obligation = PendingPredicateObligation<'tcx>;
226225
type Error = FulfillmentErrorCode<'tcx>;
226+
type OUT = Outcome<Self::Obligation, Self::Error>;
227227

228228
/// Identifies whether a predicate obligation needs processing.
229229
///
@@ -594,14 +594,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
594594
&mut self,
595595
cycle: I,
596596
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
597-
) where
597+
) -> Result<(), FulfillmentErrorCode<'tcx>>
598+
where
598599
I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
599600
{
600601
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
601602
debug!("process_child_obligations: coinductive match");
603+
Ok(())
602604
} else {
603605
let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
604-
self.selcx.infcx().report_overflow_error_cycle(&cycle);
606+
Err(FulfillmentErrorCode::CodeCycle(cycle))
605607
}
606608
}
607609
}

compiler/rustc_trait_selection/src/traits/select/mod.rs

+2-14
Original file line numberDiff line numberDiff line change
@@ -226,27 +226,15 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
226226
}
227227

228228
pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> {
229-
SelectionContext {
230-
infcx,
231-
freshener: infcx.freshener_keep_static(),
232-
intercrate: true,
233-
intercrate_ambiguity_causes: None,
234-
query_mode: TraitQueryMode::Standard,
235-
}
229+
SelectionContext { intercrate: true, ..SelectionContext::new(infcx) }
236230
}
237231

238232
pub fn with_query_mode(
239233
infcx: &'cx InferCtxt<'cx, 'tcx>,
240234
query_mode: TraitQueryMode,
241235
) -> SelectionContext<'cx, 'tcx> {
242236
debug!(?query_mode, "with_query_mode");
243-
SelectionContext {
244-
infcx,
245-
freshener: infcx.freshener_keep_static(),
246-
intercrate: false,
247-
intercrate_ambiguity_causes: None,
248-
query_mode,
249-
}
237+
SelectionContext { query_mode, ..SelectionContext::new(infcx) }
250238
}
251239

252240
/// Enables tracking of intercrate ambiguity causes. See

src/test/rustdoc-ui/normalize-cycle.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// check-pass
2+
// compile-flags: -Znormalize-docs
23
// Regression test for <https://github.com/rust-lang/rust/issues/79459>.
34
pub trait Query {}
45

0 commit comments

Comments
 (0)