mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
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: > 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.
This commit is contained in:
parent
749dec6451
commit
1512ce5925
@ -115,7 +115,11 @@ pub trait ObligationProcessor {
|
||||
/// In other words, if we had O1 which required O2 which required
|
||||
/// O3 which required O1, we would give an iterator yielding O1,
|
||||
/// O2, O3 (O1 is not yielded twice).
|
||||
fn process_backedge<'c, I>(&mut self, cycle: I, _marker: PhantomData<&'c Self::Obligation>)
|
||||
fn process_backedge<'c, I>(
|
||||
&mut self,
|
||||
cycle: I,
|
||||
_marker: PhantomData<&'c Self::Obligation>,
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
I: Clone + Iterator<Item = &'c Self::Obligation>;
|
||||
}
|
||||
@ -406,12 +410,11 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
|
||||
/// Performs a fixpoint computation over the obligation list.
|
||||
#[inline(never)]
|
||||
pub fn process_obligations<P, OUT>(&mut self, processor: &mut P) -> OUT
|
||||
pub fn process_obligations<P>(&mut self, processor: &mut P) -> P::OUT
|
||||
where
|
||||
P: ObligationProcessor<Obligation = O>,
|
||||
OUT: OutcomeTrait<Obligation = O, Error = Error<O, P::Error>>,
|
||||
{
|
||||
let mut outcome = OUT::new();
|
||||
let mut outcome = P::OUT::new();
|
||||
|
||||
// Fixpoint computation: we repeat until the inner loop stalls.
|
||||
loop {
|
||||
@ -477,7 +480,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
}
|
||||
|
||||
self.mark_successes();
|
||||
self.process_cycles(processor);
|
||||
self.process_cycles(processor, &mut outcome);
|
||||
self.compress(|obl| outcome.record_completed(obl));
|
||||
}
|
||||
|
||||
@ -562,7 +565,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
|
||||
/// Report cycles between all `Success` nodes, and convert all `Success`
|
||||
/// nodes to `Done`. This must be called after `mark_successes`.
|
||||
fn process_cycles<P>(&mut self, processor: &mut P)
|
||||
fn process_cycles<P>(&mut self, processor: &mut P, outcome: &mut P::OUT)
|
||||
where
|
||||
P: ObligationProcessor<Obligation = O>,
|
||||
{
|
||||
@ -572,7 +575,7 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
// to handle the no-op cases immediately to avoid the cost of the
|
||||
// function call.
|
||||
if node.state.get() == NodeState::Success {
|
||||
self.find_cycles_from_node(&mut stack, processor, index);
|
||||
self.find_cycles_from_node(&mut stack, processor, index, outcome);
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,8 +583,13 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
self.reused_node_vec = stack;
|
||||
}
|
||||
|
||||
fn find_cycles_from_node<P>(&self, stack: &mut Vec<usize>, processor: &mut P, index: usize)
|
||||
where
|
||||
fn find_cycles_from_node<P>(
|
||||
&self,
|
||||
stack: &mut Vec<usize>,
|
||||
processor: &mut P,
|
||||
index: usize,
|
||||
outcome: &mut P::OUT,
|
||||
) where
|
||||
P: ObligationProcessor<Obligation = O>,
|
||||
{
|
||||
let node = &self.nodes[index];
|
||||
@ -590,17 +598,20 @@ impl<O: ForestObligation> ObligationForest<O> {
|
||||
None => {
|
||||
stack.push(index);
|
||||
for &dep_index in node.dependents.iter() {
|
||||
self.find_cycles_from_node(stack, processor, dep_index);
|
||||
self.find_cycles_from_node(stack, processor, dep_index, outcome);
|
||||
}
|
||||
stack.pop();
|
||||
node.state.set(NodeState::Done);
|
||||
}
|
||||
Some(rpos) => {
|
||||
// Cycle detected.
|
||||
processor.process_backedge(
|
||||
let result = processor.process_backedge(
|
||||
stack[rpos..].iter().map(|&i| &self.nodes[i].obligation),
|
||||
PhantomData,
|
||||
);
|
||||
if let Err(err) = result {
|
||||
outcome.record_error(Error { error: err, backtrace: self.error_at(index) });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -77,10 +77,15 @@ where
|
||||
(self.process_obligation)(obligation)
|
||||
}
|
||||
|
||||
fn process_backedge<'c, I>(&mut self, _cycle: I, _marker: PhantomData<&'c Self::Obligation>)
|
||||
fn process_backedge<'c, I>(
|
||||
&mut self,
|
||||
_cycle: I,
|
||||
_marker: PhantomData<&'c Self::Obligation>,
|
||||
) -> Result<(), Self::Error>
|
||||
where
|
||||
I: Clone + Iterator<Item = &'c Self::Obligation>,
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,6 +105,8 @@ pub struct FulfillmentError<'tcx> {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum FulfillmentErrorCode<'tcx> {
|
||||
/// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented.
|
||||
CodeCycle(Vec<Obligation<'tcx, ty::Predicate<'tcx>>>),
|
||||
CodeSelectionError(SelectionError<'tcx>),
|
||||
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
|
||||
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
|
||||
|
@ -47,6 +47,7 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
|
||||
write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
|
||||
}
|
||||
super::CodeAmbiguity => write!(f, "Ambiguity"),
|
||||
super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,12 @@
|
||||
// general routines.
|
||||
|
||||
use crate::infer::{DefiningAnchor, TyCtxtInferExt};
|
||||
use crate::traits::error_reporting::InferCtxtExt;
|
||||
use crate::traits::{
|
||||
ImplSource, Obligation, ObligationCause, SelectionContext, TraitEngine, TraitEngineExt,
|
||||
Unimplemented,
|
||||
};
|
||||
use rustc_infer::traits::FulfillmentErrorCode;
|
||||
use rustc_middle::traits::CodegenObligationError;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
|
||||
@ -62,6 +64,14 @@ pub fn codegen_select_candidate<'tcx>(
|
||||
// optimization to stop iterating early.
|
||||
let errors = fulfill_cx.select_all_or_error(&infcx);
|
||||
if !errors.is_empty() {
|
||||
// `rustc_monomorphize::collector` assumes there are no type errors.
|
||||
// Cycle errors are the only post-monomorphization errors possible; emit them now so
|
||||
// `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
|
||||
for err in errors {
|
||||
if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
|
||||
infcx.report_overflow_error_cycle(&cycle);
|
||||
}
|
||||
}
|
||||
return Err(CodegenObligationError::FulfillmentError);
|
||||
}
|
||||
|
||||
|
@ -1540,6 +1540,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
|
||||
}
|
||||
diag.emit();
|
||||
}
|
||||
FulfillmentErrorCode::CodeCycle(ref cycle) => {
|
||||
self.report_overflow_error_cycle(cycle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,9 @@ use super::Unimplemented;
|
||||
use super::{FulfillmentError, FulfillmentErrorCode};
|
||||
use super::{ObligationCause, PredicateObligation};
|
||||
|
||||
use crate::traits::error_reporting::InferCtxtExt as _;
|
||||
use crate::traits::project::PolyProjectionObligation;
|
||||
use crate::traits::project::ProjectionCacheKeyExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use crate::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
||||
impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
|
||||
/// Note that we include both the `ParamEnv` and the `Predicate`,
|
||||
@ -603,14 +602,16 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> {
|
||||
&mut self,
|
||||
cycle: I,
|
||||
_marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
|
||||
) where
|
||||
) -> Result<(), FulfillmentErrorCode<'tcx>>
|
||||
where
|
||||
I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
|
||||
{
|
||||
if self.selcx.coinductive_match(cycle.clone().map(|s| s.obligation.predicate)) {
|
||||
debug!("process_child_obligations: coinductive match");
|
||||
Ok(())
|
||||
} else {
|
||||
let cycle: Vec<_> = cycle.map(|c| c.obligation.clone()).collect();
|
||||
self.selcx.infcx().report_overflow_error_cycle(&cycle);
|
||||
Err(FulfillmentErrorCode::CodeCycle(cycle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
// check-pass
|
||||
// compile-flags: -Znormalize-docs
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/79459>.
|
||||
pub trait Query {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user