From 5922d6cf60169ad0fd792581c9da11bd633533da Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 18:43:15 -0500 Subject: [PATCH 1/3] slightly cleanup building SelectionContext --- .../src/traits/select/mod.rs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8b15e10ba9cbf..6bdd613be5aa7 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -226,13 +226,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } pub fn intercrate(infcx: &'cx InferCtxt<'cx, 'tcx>) -> SelectionContext<'cx, 'tcx> { - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: true, - intercrate_ambiguity_causes: None, - query_mode: TraitQueryMode::Standard, - } + SelectionContext { intercrate: true, ..SelectionContext::new(infcx) } } pub fn with_query_mode( @@ -240,13 +234,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { query_mode: TraitQueryMode, ) -> SelectionContext<'cx, 'tcx> { debug!(?query_mode, "with_query_mode"); - SelectionContext { - infcx, - freshener: infcx.freshener_keep_static(), - intercrate: false, - intercrate_ambiguity_causes: None, - query_mode, - } + SelectionContext { query_mode, ..SelectionContext::new(infcx) } } /// Enables tracking of intercrate ambiguity causes. See From 749dec64519b5bdfe688cb945eeee5afd6ab68d0 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 20:57:37 -0500 Subject: [PATCH 2/3] Make `OUT` an associated type instead of a generic parameter This avoids toil when changing other functions in `ObligationForest` to take an `OUT` parameter. --- compiler/rustc_data_structures/src/obligation_forest/mod.rs | 4 ++++ compiler/rustc_data_structures/src/obligation_forest/tests.rs | 1 + compiler/rustc_trait_selection/src/traits/fulfill.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index e351b650a16c1..f2d72647a6686 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -95,6 +95,10 @@ pub trait ForestObligation: Clone + Debug { pub trait ObligationProcessor { type Obligation: ForestObligation; type Error: Debug; + type OUT: OutcomeTrait< + Obligation = Self::Obligation, + Error = Error, + >; fn needs_process_obligation(&self, obligation: &Self::Obligation) -> bool; diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index e2991aae1742c..f2a04796691ac 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -64,6 +64,7 @@ where { type Obligation = O; type Error = E; + type OUT = TestOutcome; fn needs_process_obligation(&self, _obligation: &Self::Obligation) -> bool { true diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 3763a98c488b7..0ea2b6ce885fd 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -224,6 +224,7 @@ fn mk_pending(os: Vec>) -> Vec ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { type Obligation = PendingPredicateObligation<'tcx>; type Error = FulfillmentErrorCode<'tcx>; + type OUT = Outcome; /// Identifies whether a predicate obligation needs processing. /// From 1512ce5925eeafc01f011c46216c157e4e5644cb Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 19 Sep 2022 20:45:00 -0500 Subject: [PATCH 3/3] 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. --- .../src/obligation_forest/mod.rs | 33 ++++++++++++------- .../src/obligation_forest/tests.rs | 7 +++- compiler/rustc_infer/src/traits/mod.rs | 2 ++ .../src/traits/structural_impls.rs | 1 + .../src/traits/codegen.rs | 10 ++++++ .../src/traits/error_reporting/mod.rs | 3 ++ .../src/traits/fulfill.rs | 9 ++--- src/test/rustdoc-ui/normalize-cycle.rs | 1 + 8 files changed, 50 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs index f2d72647a6686..10e673cd9297b 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs @@ -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; } @@ -406,12 +410,11 @@ impl ObligationForest { /// Performs a fixpoint computation over the obligation list. #[inline(never)] - pub fn process_obligations(&mut self, processor: &mut P) -> OUT + pub fn process_obligations

(&mut self, processor: &mut P) -> P::OUT where P: ObligationProcessor, - OUT: OutcomeTrait>, { - 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 ObligationForest { } 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 ObligationForest { /// Report cycles between all `Success` nodes, and convert all `Success` /// nodes to `Done`. This must be called after `mark_successes`. - fn process_cycles

(&mut self, processor: &mut P) + fn process_cycles

(&mut self, processor: &mut P, outcome: &mut P::OUT) where P: ObligationProcessor, { @@ -572,7 +575,7 @@ impl ObligationForest { // 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 ObligationForest { self.reused_node_vec = stack; } - fn find_cycles_from_node

(&self, stack: &mut Vec, processor: &mut P, index: usize) - where + fn find_cycles_from_node

( + &self, + stack: &mut Vec, + processor: &mut P, + index: usize, + outcome: &mut P::OUT, + ) where P: ObligationProcessor, { let node = &self.nodes[index]; @@ -590,17 +598,20 @@ impl ObligationForest { 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) }); + } } } } diff --git a/compiler/rustc_data_structures/src/obligation_forest/tests.rs b/compiler/rustc_data_structures/src/obligation_forest/tests.rs index f2a04796691ac..bc252f772a168 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/tests.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/tests.rs @@ -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, { + Ok(()) } } diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs index 4df4de21a0f07..ed2beefc6e773 100644 --- a/compiler/rustc_infer/src/traits/mod.rs +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -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>>), CodeSelectionError(SelectionError<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeSubtypeError(ExpectedFound>, TypeError<'tcx>), // always comes from a SubtypePredicate diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs index 573d2d1e33016..1c6ab6a082b99 100644 --- a/compiler/rustc_infer/src/traits/structural_impls.rs +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -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), } } } diff --git a/compiler/rustc_trait_selection/src/traits/codegen.rs b/compiler/rustc_trait_selection/src/traits/codegen.rs index 08adbcbd410c6..0155798c8b6d7 100644 --- a/compiler/rustc_trait_selection/src/traits/codegen.rs +++ b/compiler/rustc_trait_selection/src/traits/codegen.rs @@ -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); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index efdb1ace13992..d62b399c1b562 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -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); + } } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 0ea2b6ce885fd..1cd8e1c21236f 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -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>, { 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)) } } } diff --git a/src/test/rustdoc-ui/normalize-cycle.rs b/src/test/rustdoc-ui/normalize-cycle.rs index 14ffac1e1dce6..1ed9ac6bc34ac 100644 --- a/src/test/rustdoc-ui/normalize-cycle.rs +++ b/src/test/rustdoc-ui/normalize-cycle.rs @@ -1,4 +1,5 @@ // check-pass +// compile-flags: -Znormalize-docs // Regression test for . pub trait Query {}