Rollup merge of #121497 - lcnr:coherence-suggest-increasing-recursion-limit, r=compiler-errors
`-Znext-solver=coherence`: suggest increasing recursion limit r? `@compiler-errors`
This commit is contained in:
commit
4d71fe7cc1
29 changed files with 341 additions and 255 deletions
|
@ -135,16 +135,18 @@ pub struct FulfillmentError<'tcx> {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum FulfillmentErrorCode<'tcx> {
|
pub enum FulfillmentErrorCode<'tcx> {
|
||||||
/// Inherently impossible to fulfill; this trait is implemented if and only if it is already implemented.
|
/// Inherently impossible to fulfill; this trait is implemented if and only
|
||||||
|
/// if it is already implemented.
|
||||||
Cycle(Vec<PredicateObligation<'tcx>>),
|
Cycle(Vec<PredicateObligation<'tcx>>),
|
||||||
SelectionError(SelectionError<'tcx>),
|
SelectionError(SelectionError<'tcx>),
|
||||||
ProjectionError(MismatchedProjectionTypes<'tcx>),
|
ProjectionError(MismatchedProjectionTypes<'tcx>),
|
||||||
SubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
|
SubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
|
||||||
ConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
|
ConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
|
||||||
Ambiguity {
|
Ambiguity {
|
||||||
/// Overflow reported from the new solver `-Znext-solver`, which will
|
/// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
|
||||||
/// be reported as an regular error as opposed to a fatal error.
|
/// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
|
||||||
overflow: bool,
|
/// emitting a fatal error instead.
|
||||||
|
overflow: Option<bool>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,10 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
|
||||||
ConstEquateError(ref a, ref b) => {
|
ConstEquateError(ref a, ref b) => {
|
||||||
write!(f, "CodeConstEquateError({a:?}, {b:?})")
|
write!(f, "CodeConstEquateError({a:?}, {b:?})")
|
||||||
}
|
}
|
||||||
Ambiguity { overflow: false } => write!(f, "Ambiguity"),
|
Ambiguity { overflow: None } => write!(f, "Ambiguity"),
|
||||||
Ambiguity { overflow: true } => write!(f, "Overflow"),
|
Ambiguity { overflow: Some(suggest_increasing_limit) } => {
|
||||||
|
write!(f, "Overflow({suggest_increasing_limit})")
|
||||||
|
}
|
||||||
Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
|
Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,6 @@ pub enum Certainty {
|
||||||
|
|
||||||
impl Certainty {
|
impl Certainty {
|
||||||
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
|
pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
|
||||||
pub const OVERFLOW: Certainty = Certainty::Maybe(MaybeCause::Overflow);
|
|
||||||
|
|
||||||
/// Use this function to merge the certainty of multiple nested subgoals.
|
/// Use this function to merge the certainty of multiple nested subgoals.
|
||||||
///
|
///
|
||||||
|
@ -79,15 +78,12 @@ impl Certainty {
|
||||||
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
|
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
|
||||||
(Certainty::Yes, Certainty::Maybe(_)) => other,
|
(Certainty::Yes, Certainty::Maybe(_)) => other,
|
||||||
(Certainty::Maybe(_), Certainty::Yes) => self,
|
(Certainty::Maybe(_), Certainty::Yes) => self,
|
||||||
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => {
|
(Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)),
|
||||||
Certainty::Maybe(MaybeCause::Ambiguity)
|
|
||||||
}
|
|
||||||
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Overflow))
|
|
||||||
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Ambiguity))
|
|
||||||
| (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
|
|
||||||
Certainty::Maybe(MaybeCause::Overflow)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn overflow(suggest_increasing_limit: bool) -> Certainty {
|
||||||
|
Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +95,21 @@ pub enum MaybeCause {
|
||||||
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
|
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
|
||||||
Ambiguity,
|
Ambiguity,
|
||||||
/// We gave up due to an overflow, most often by hitting the recursion limit.
|
/// We gave up due to an overflow, most often by hitting the recursion limit.
|
||||||
Overflow,
|
Overflow { suggest_increasing_limit: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaybeCause {
|
||||||
|
fn unify_with(self, other: MaybeCause) -> MaybeCause {
|
||||||
|
match (self, other) {
|
||||||
|
(MaybeCause::Ambiguity, MaybeCause::Ambiguity) => MaybeCause::Ambiguity,
|
||||||
|
(MaybeCause::Ambiguity, MaybeCause::Overflow { .. }) => other,
|
||||||
|
(MaybeCause::Overflow { .. }, MaybeCause::Ambiguity) => self,
|
||||||
|
(
|
||||||
|
MaybeCause::Overflow { suggest_increasing_limit: a },
|
||||||
|
MaybeCause::Overflow { suggest_increasing_limit: b },
|
||||||
|
) => MaybeCause::Overflow { suggest_increasing_limit: a || b },
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||||
|
|
|
@ -36,11 +36,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
|
||||||
|
|
||||||
let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
|
let Some(lhs) = self.try_normalize_term(param_env, lhs)? else {
|
||||||
return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
|
return self
|
||||||
|
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
|
let Some(rhs) = self.try_normalize_term(param_env, rhs)? else {
|
||||||
return self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
|
return self
|
||||||
|
.evaluate_added_goals_and_make_canonical_response(Certainty::overflow(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
let variance = match direction {
|
let variance = match direction {
|
||||||
|
|
|
@ -7,6 +7,7 @@ use rustc_infer::infer::{
|
||||||
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
|
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
|
||||||
};
|
};
|
||||||
use rustc_infer::traits::query::NoSolution;
|
use rustc_infer::traits::query::NoSolution;
|
||||||
|
use rustc_infer::traits::solve::MaybeCause;
|
||||||
use rustc_infer::traits::ObligationCause;
|
use rustc_infer::traits::ObligationCause;
|
||||||
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
use rustc_middle::infer::canonical::CanonicalVarInfos;
|
||||||
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
|
||||||
|
@ -29,7 +30,7 @@ use std::ops::ControlFlow;
|
||||||
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment};
|
||||||
|
|
||||||
use super::inspect::ProofTreeBuilder;
|
use super::inspect::ProofTreeBuilder;
|
||||||
use super::{search_graph, GoalEvaluationKind};
|
use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
|
||||||
use super::{search_graph::SearchGraph, Goal};
|
use super::{search_graph::SearchGraph, Goal};
|
||||||
use super::{GoalSource, SolverMode};
|
use super::{GoalSource, SolverMode};
|
||||||
pub use select::InferCtxtSelectExt;
|
pub use select::InferCtxtSelectExt;
|
||||||
|
@ -154,10 +155,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
self.search_graph.solver_mode()
|
self.search_graph.solver_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn local_overflow_limit(&self) -> usize {
|
|
||||||
self.search_graph.local_overflow_limit()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a root evaluation context and search graph. This should only be
|
/// Creates a root evaluation context and search graph. This should only be
|
||||||
/// used from outside of any evaluation, and other methods should be preferred
|
/// used from outside of any evaluation, and other methods should be preferred
|
||||||
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
|
/// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
|
||||||
|
@ -167,7 +164,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
|
f: impl FnOnce(&mut EvalCtxt<'_, 'tcx>) -> R,
|
||||||
) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
|
) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
|
||||||
let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
|
||||||
let mut search_graph = search_graph::SearchGraph::new(infcx.tcx, mode);
|
let mut search_graph = search_graph::SearchGraph::new(mode);
|
||||||
|
|
||||||
let mut ecx = EvalCtxt {
|
let mut ecx = EvalCtxt {
|
||||||
search_graph: &mut search_graph,
|
search_graph: &mut search_graph,
|
||||||
|
@ -388,9 +385,12 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
&& source != GoalSource::ImplWhereBound
|
&& source != GoalSource::ImplWhereBound
|
||||||
};
|
};
|
||||||
|
|
||||||
if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() {
|
if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty
|
||||||
(Certainty::OVERFLOW, false)
|
&& !keep_overflow_constraints()
|
||||||
} else {
|
{
|
||||||
|
return (response.value.certainty, false);
|
||||||
|
}
|
||||||
|
|
||||||
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|
||||||
|| !response.value.external_constraints.opaque_types.is_empty();
|
|| !response.value.external_constraints.opaque_types.is_empty();
|
||||||
|
|
||||||
|
@ -398,7 +398,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
self.instantiate_and_apply_query_response(param_env, original_values, response);
|
self.instantiate_and_apply_query_response(param_env, original_values, response);
|
||||||
(certainty, has_changed)
|
(certainty, has_changed)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
|
fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
|
||||||
let Goal { param_env, predicate } = goal;
|
let Goal { param_env, predicate } = goal;
|
||||||
|
@ -466,8 +465,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
|
||||||
let inspect = self.inspect.new_evaluate_added_goals();
|
let inspect = self.inspect.new_evaluate_added_goals();
|
||||||
let inspect = core::mem::replace(&mut self.inspect, inspect);
|
let inspect = core::mem::replace(&mut self.inspect, inspect);
|
||||||
|
|
||||||
let mut response = Ok(Certainty::OVERFLOW);
|
let mut response = Ok(Certainty::overflow(false));
|
||||||
for _ in 0..self.local_overflow_limit() {
|
for _ in 0..FIXPOINT_STEP_LIMIT {
|
||||||
// FIXME: This match is a bit ugly, it might be nice to change the inspect
|
// FIXME: This match is a bit ugly, it might be nice to change the inspect
|
||||||
// stuff to use a closure instead. which should hopefully simplify this a bit.
|
// stuff to use a closure instead. which should hopefully simplify this a bit.
|
||||||
match self.evaluate_added_goals_step() {
|
match self.evaluate_added_goals_step() {
|
||||||
|
|
|
@ -24,7 +24,7 @@ use super::{Certainty, InferCtxtEvalExt};
|
||||||
/// It is also likely that we want to use slightly different datastructures
|
/// It is also likely that we want to use slightly different datastructures
|
||||||
/// here as this will have to deal with far more root goals than `evaluate_all`.
|
/// here as this will have to deal with far more root goals than `evaluate_all`.
|
||||||
pub struct FulfillmentCtxt<'tcx> {
|
pub struct FulfillmentCtxt<'tcx> {
|
||||||
obligations: Vec<PredicateObligation<'tcx>>,
|
obligations: ObligationStorage<'tcx>,
|
||||||
|
|
||||||
/// The snapshot in which this context was created. Using the context
|
/// The snapshot in which this context was created. Using the context
|
||||||
/// outside of this snapshot leads to subtle bugs if the snapshot
|
/// outside of this snapshot leads to subtle bugs if the snapshot
|
||||||
|
@ -33,6 +33,57 @@ pub struct FulfillmentCtxt<'tcx> {
|
||||||
usable_in_snapshot: usize,
|
usable_in_snapshot: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ObligationStorage<'tcx> {
|
||||||
|
/// Obligations which resulted in an overflow in fulfillment itself.
|
||||||
|
///
|
||||||
|
/// We cannot eagerly return these as error so we instead store them here
|
||||||
|
/// to avoid recomputing them each time `select_where_possible` is called.
|
||||||
|
/// This also allows us to return the correct `FulfillmentError` for them.
|
||||||
|
overflowed: Vec<PredicateObligation<'tcx>>,
|
||||||
|
pending: Vec<PredicateObligation<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> ObligationStorage<'tcx> {
|
||||||
|
fn register(&mut self, obligation: PredicateObligation<'tcx>) {
|
||||||
|
self.pending.push(obligation);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_pending(&self) -> Vec<PredicateObligation<'tcx>> {
|
||||||
|
let mut obligations = self.pending.clone();
|
||||||
|
obligations.extend(self.overflowed.iter().cloned());
|
||||||
|
obligations
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_pending(&mut self) -> Vec<PredicateObligation<'tcx>> {
|
||||||
|
let mut obligations = mem::take(&mut self.pending);
|
||||||
|
obligations.extend(self.overflowed.drain(..));
|
||||||
|
obligations
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> {
|
||||||
|
mem::take(&mut self.pending).into_iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
|
||||||
|
infcx.probe(|_| {
|
||||||
|
// IMPORTANT: we must not use solve any inference variables in the obligations
|
||||||
|
// as this is all happening inside of a probe. We use a probe to make sure
|
||||||
|
// we get all obligations involved in the overflow. We pretty much check: if
|
||||||
|
// we were to do another step of `select_where_possible`, which goals would
|
||||||
|
// change.
|
||||||
|
self.overflowed.extend(self.pending.extract_if(|o| {
|
||||||
|
let goal = o.clone().into();
|
||||||
|
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::Never).0;
|
||||||
|
match result {
|
||||||
|
Ok((has_changed, _)) => has_changed,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> FulfillmentCtxt<'tcx> {
|
impl<'tcx> FulfillmentCtxt<'tcx> {
|
||||||
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
|
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
|
||||||
assert!(
|
assert!(
|
||||||
|
@ -40,7 +91,10 @@ impl<'tcx> FulfillmentCtxt<'tcx> {
|
||||||
"new trait solver fulfillment context created when \
|
"new trait solver fulfillment context created when \
|
||||||
infcx is set up for old trait solver"
|
infcx is set up for old trait solver"
|
||||||
);
|
);
|
||||||
FulfillmentCtxt { obligations: Vec::new(), usable_in_snapshot: infcx.num_open_snapshots() }
|
FulfillmentCtxt {
|
||||||
|
obligations: Default::default(),
|
||||||
|
usable_in_snapshot: infcx.num_open_snapshots(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inspect_evaluated_obligation(
|
fn inspect_evaluated_obligation(
|
||||||
|
@ -67,40 +121,24 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
||||||
obligation: PredicateObligation<'tcx>,
|
obligation: PredicateObligation<'tcx>,
|
||||||
) {
|
) {
|
||||||
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
|
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
|
||||||
self.obligations.push(obligation);
|
self.obligations.register(obligation);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
||||||
self.obligations
|
let mut errors: Vec<_> = self
|
||||||
|
.obligations
|
||||||
|
.pending
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|obligation| {
|
.map(|obligation| fulfillment_error_for_stalled(infcx, obligation))
|
||||||
let code = infcx.probe(|_| {
|
.collect();
|
||||||
match infcx
|
|
||||||
.evaluate_root_goal(obligation.clone().into(), GenerateProofTree::IfEnabled)
|
|
||||||
.0
|
|
||||||
{
|
|
||||||
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
|
|
||||||
FulfillmentErrorCode::Ambiguity { overflow: false }
|
|
||||||
}
|
|
||||||
Ok((_, Certainty::Maybe(MaybeCause::Overflow))) => {
|
|
||||||
FulfillmentErrorCode::Ambiguity { overflow: true }
|
|
||||||
}
|
|
||||||
Ok((_, Certainty::Yes)) => {
|
|
||||||
bug!("did not expect successful goal when collecting ambiguity errors")
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
bug!("did not expect selection error when collecting ambiguity errors")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
FulfillmentError {
|
errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError {
|
||||||
obligation: obligation.clone(),
|
root_obligation: obligation.clone(),
|
||||||
code,
|
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
|
||||||
root_obligation: obligation,
|
obligation,
|
||||||
}
|
}));
|
||||||
})
|
|
||||||
.collect()
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
|
||||||
|
@ -108,79 +146,27 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
for i in 0.. {
|
for i in 0.. {
|
||||||
if !infcx.tcx.recursion_limit().value_within_limit(i) {
|
if !infcx.tcx.recursion_limit().value_within_limit(i) {
|
||||||
// Only return true errors that we have accumulated while processing;
|
self.obligations.on_fulfillment_overflow(infcx);
|
||||||
// keep ambiguities around, *including overflows*, because they shouldn't
|
// Only return true errors that we have accumulated while processing.
|
||||||
// be considered true errors.
|
|
||||||
return errors;
|
return errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut has_changed = false;
|
let mut has_changed = false;
|
||||||
for obligation in mem::take(&mut self.obligations) {
|
for obligation in self.obligations.unstalled_for_select() {
|
||||||
let goal = obligation.clone().into();
|
let goal = obligation.clone().into();
|
||||||
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
|
let result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
|
||||||
self.inspect_evaluated_obligation(infcx, &obligation, &result);
|
self.inspect_evaluated_obligation(infcx, &obligation, &result);
|
||||||
let (changed, certainty) = match result {
|
let (changed, certainty) = match result {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(NoSolution) => {
|
Err(NoSolution) => {
|
||||||
errors.push(FulfillmentError {
|
errors.push(fulfillment_error_for_no_solution(infcx, obligation));
|
||||||
obligation: obligation.clone(),
|
|
||||||
code: match goal.predicate.kind().skip_binder() {
|
|
||||||
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
|
|
||||||
FulfillmentErrorCode::ProjectionError(
|
|
||||||
// FIXME: This could be a `Sorts` if the term is a type
|
|
||||||
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::NormalizesTo(..) => {
|
|
||||||
FulfillmentErrorCode::ProjectionError(
|
|
||||||
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::AliasRelate(_, _, _) => {
|
|
||||||
FulfillmentErrorCode::ProjectionError(
|
|
||||||
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::Subtype(pred) => {
|
|
||||||
let (a, b) = infcx.enter_forall_and_leak_universe(
|
|
||||||
goal.predicate.kind().rebind((pred.a, pred.b)),
|
|
||||||
);
|
|
||||||
let expected_found = ExpectedFound::new(true, a, b);
|
|
||||||
FulfillmentErrorCode::SubtypeError(
|
|
||||||
expected_found,
|
|
||||||
TypeError::Sorts(expected_found),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::Coerce(pred) => {
|
|
||||||
let (a, b) = infcx.enter_forall_and_leak_universe(
|
|
||||||
goal.predicate.kind().rebind((pred.a, pred.b)),
|
|
||||||
);
|
|
||||||
let expected_found = ExpectedFound::new(false, a, b);
|
|
||||||
FulfillmentErrorCode::SubtypeError(
|
|
||||||
expected_found,
|
|
||||||
TypeError::Sorts(expected_found),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::Clause(_)
|
|
||||||
| ty::PredicateKind::ObjectSafe(_)
|
|
||||||
| ty::PredicateKind::Ambiguous => {
|
|
||||||
FulfillmentErrorCode::SelectionError(
|
|
||||||
SelectionError::Unimplemented,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ty::PredicateKind::ConstEquate(..) => {
|
|
||||||
bug!("unexpected goal: {goal:?}")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
root_obligation: obligation,
|
|
||||||
});
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
has_changed |= changed;
|
has_changed |= changed;
|
||||||
match certainty {
|
match certainty {
|
||||||
Certainty::Yes => {}
|
Certainty::Yes => {}
|
||||||
Certainty::Maybe(_) => self.obligations.push(obligation),
|
Certainty::Maybe(_) => self.obligations.register(obligation),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,13 +179,84 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
|
||||||
self.obligations.clone()
|
self.obligations.clone_pending()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain_unstalled_obligations(
|
fn drain_unstalled_obligations(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: &InferCtxt<'tcx>,
|
_: &InferCtxt<'tcx>,
|
||||||
) -> Vec<PredicateObligation<'tcx>> {
|
) -> Vec<PredicateObligation<'tcx>> {
|
||||||
std::mem::take(&mut self.obligations)
|
self.obligations.take_pending()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fulfillment_error_for_no_solution<'tcx>(
|
||||||
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
obligation: PredicateObligation<'tcx>,
|
||||||
|
) -> FulfillmentError<'tcx> {
|
||||||
|
let code = match obligation.predicate.kind().skip_binder() {
|
||||||
|
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
|
||||||
|
FulfillmentErrorCode::ProjectionError(
|
||||||
|
// FIXME: This could be a `Sorts` if the term is a type
|
||||||
|
MismatchedProjectionTypes { err: TypeError::Mismatch },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ty::PredicateKind::NormalizesTo(..) => {
|
||||||
|
FulfillmentErrorCode::ProjectionError(MismatchedProjectionTypes {
|
||||||
|
err: TypeError::Mismatch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ty::PredicateKind::AliasRelate(_, _, _) => {
|
||||||
|
FulfillmentErrorCode::ProjectionError(MismatchedProjectionTypes {
|
||||||
|
err: TypeError::Mismatch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ty::PredicateKind::Subtype(pred) => {
|
||||||
|
let (a, b) = infcx.enter_forall_and_leak_universe(
|
||||||
|
obligation.predicate.kind().rebind((pred.a, pred.b)),
|
||||||
|
);
|
||||||
|
let expected_found = ExpectedFound::new(true, a, b);
|
||||||
|
FulfillmentErrorCode::SubtypeError(expected_found, TypeError::Sorts(expected_found))
|
||||||
|
}
|
||||||
|
ty::PredicateKind::Coerce(pred) => {
|
||||||
|
let (a, b) = infcx.enter_forall_and_leak_universe(
|
||||||
|
obligation.predicate.kind().rebind((pred.a, pred.b)),
|
||||||
|
);
|
||||||
|
let expected_found = ExpectedFound::new(false, a, b);
|
||||||
|
FulfillmentErrorCode::SubtypeError(expected_found, TypeError::Sorts(expected_found))
|
||||||
|
}
|
||||||
|
ty::PredicateKind::Clause(_)
|
||||||
|
| ty::PredicateKind::ObjectSafe(_)
|
||||||
|
| ty::PredicateKind::Ambiguous => {
|
||||||
|
FulfillmentErrorCode::SelectionError(SelectionError::Unimplemented)
|
||||||
|
}
|
||||||
|
ty::PredicateKind::ConstEquate(..) => {
|
||||||
|
bug!("unexpected goal: {obligation:?}")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FulfillmentError { root_obligation: obligation.clone(), code, obligation }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fulfillment_error_for_stalled<'tcx>(
|
||||||
|
infcx: &InferCtxt<'tcx>,
|
||||||
|
obligation: PredicateObligation<'tcx>,
|
||||||
|
) -> FulfillmentError<'tcx> {
|
||||||
|
let code = infcx.probe(|_| {
|
||||||
|
match infcx.evaluate_root_goal(obligation.clone().into(), GenerateProofTree::Never).0 {
|
||||||
|
Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
|
||||||
|
FulfillmentErrorCode::Ambiguity { overflow: None }
|
||||||
|
}
|
||||||
|
Ok((_, Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit }))) => {
|
||||||
|
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) }
|
||||||
|
}
|
||||||
|
Ok((_, Certainty::Yes)) => {
|
||||||
|
bug!("did not expect successful goal when collecting ambiguity errors")
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
bug!("did not expect selection error when collecting ambiguity errors")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
FulfillmentError { obligation: obligation.clone(), code, root_obligation: obligation }
|
||||||
|
}
|
||||||
|
|
|
@ -42,6 +42,17 @@ pub use fulfill::FulfillmentCtxt;
|
||||||
pub(crate) use normalize::deeply_normalize_for_diagnostics;
|
pub(crate) use normalize::deeply_normalize_for_diagnostics;
|
||||||
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
|
pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
|
||||||
|
|
||||||
|
/// How many fixpoint iterations we should attempt inside of the solver before bailing
|
||||||
|
/// with overflow.
|
||||||
|
///
|
||||||
|
/// We previously used `tcx.recursion_limit().0.checked_ilog2().unwrap_or(0)` for this.
|
||||||
|
/// However, it feels unlikely that uncreasing the recursion limit by a power of two
|
||||||
|
/// to get one more itereation is every useful or desirable. We now instead used a constant
|
||||||
|
/// here. If there ever ends up some use-cases where a bigger number of fixpoint iterations
|
||||||
|
/// is required, we can add a new attribute for that or revert this to be dependant on the
|
||||||
|
/// recursion limit again. However, this feels very unlikely.
|
||||||
|
const FIXPOINT_STEP_LIMIT: usize = 8;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum SolverMode {
|
enum SolverMode {
|
||||||
/// Ordinary trait solving, using everywhere except for coherence.
|
/// Ordinary trait solving, using everywhere except for coherence.
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use crate::solve::FIXPOINT_STEP_LIMIT;
|
||||||
|
|
||||||
use super::inspect;
|
use super::inspect;
|
||||||
use super::inspect::ProofTreeBuilder;
|
use super::inspect::ProofTreeBuilder;
|
||||||
use super::SolverMode;
|
use super::SolverMode;
|
||||||
|
@ -99,7 +101,6 @@ impl<'tcx> ProvisionalCacheEntry<'tcx> {
|
||||||
|
|
||||||
pub(super) struct SearchGraph<'tcx> {
|
pub(super) struct SearchGraph<'tcx> {
|
||||||
mode: SolverMode,
|
mode: SolverMode,
|
||||||
local_overflow_limit: usize,
|
|
||||||
/// The stack of goals currently being computed.
|
/// The stack of goals currently being computed.
|
||||||
///
|
///
|
||||||
/// An element is *deeper* in the stack if its index is *lower*.
|
/// An element is *deeper* in the stack if its index is *lower*.
|
||||||
|
@ -116,10 +117,9 @@ pub(super) struct SearchGraph<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> SearchGraph<'tcx> {
|
impl<'tcx> SearchGraph<'tcx> {
|
||||||
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
|
pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> {
|
||||||
Self {
|
Self {
|
||||||
mode,
|
mode,
|
||||||
local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize,
|
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
provisional_cache: Default::default(),
|
provisional_cache: Default::default(),
|
||||||
cycle_participants: Default::default(),
|
cycle_participants: Default::default(),
|
||||||
|
@ -130,10 +130,6 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
self.mode
|
self.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn local_overflow_limit(&self) -> usize {
|
|
||||||
self.local_overflow_limit
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Update the stack and reached depths on cache hits.
|
/// Update the stack and reached depths on cache hits.
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
|
fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) {
|
||||||
|
@ -277,7 +273,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
|
inspect.goal_evaluation_kind(inspect::WipCanonicalGoalEvaluationKind::Overflow);
|
||||||
return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
|
return Self::response_no_constraints(tcx, input, Certainty::overflow(true));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try to fetch the goal from the global cache.
|
// Try to fetch the goal from the global cache.
|
||||||
|
@ -370,7 +366,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
} else if is_coinductive_cycle {
|
} else if is_coinductive_cycle {
|
||||||
Self::response_no_constraints(tcx, input, Certainty::Yes)
|
Self::response_no_constraints(tcx, input, Certainty::Yes)
|
||||||
} else {
|
} else {
|
||||||
Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)
|
Self::response_no_constraints(tcx, input, Certainty::overflow(false))
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// No entry, we push this goal on the stack and try to prove it.
|
// No entry, we push this goal on the stack and try to prove it.
|
||||||
|
@ -398,7 +394,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
// of this we continuously recompute the cycle until the result
|
// of this we continuously recompute the cycle until the result
|
||||||
// of the previous iteration is equal to the final result, at which
|
// of the previous iteration is equal to the final result, at which
|
||||||
// point we are done.
|
// point we are done.
|
||||||
for _ in 0..self.local_overflow_limit() {
|
for _ in 0..FIXPOINT_STEP_LIMIT {
|
||||||
let result = prove_goal(self, inspect);
|
let result = prove_goal(self, inspect);
|
||||||
let stack_entry = self.pop_stack();
|
let stack_entry = self.pop_stack();
|
||||||
debug_assert_eq!(stack_entry.input, input);
|
debug_assert_eq!(stack_entry.input, input);
|
||||||
|
@ -431,7 +427,8 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
} else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
|
} else if stack_entry.has_been_used == HasBeenUsed::COINDUCTIVE_CYCLE {
|
||||||
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
|
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
|
||||||
} else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
|
} else if stack_entry.has_been_used == HasBeenUsed::INDUCTIVE_CYCLE {
|
||||||
Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) == result
|
Self::response_no_constraints(tcx, input, Certainty::overflow(false))
|
||||||
|
== result
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
@ -452,7 +449,7 @@ impl<'tcx> SearchGraph<'tcx> {
|
||||||
debug!("canonical cycle overflow");
|
debug!("canonical cycle overflow");
|
||||||
let current_entry = self.pop_stack();
|
let current_entry = self.pop_stack();
|
||||||
debug_assert!(current_entry.has_been_used.is_empty());
|
debug_assert!(current_entry.has_been_used.is_empty());
|
||||||
let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW);
|
let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false));
|
||||||
(current_entry, result)
|
(current_entry, result)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ use rustc_errors::{Diag, EmissionGuarantee};
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||||
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
|
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::{util, TraitEngine, TraitEngineExt};
|
use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
|
||||||
use rustc_middle::traits::query::NoSolution;
|
use rustc_middle::traits::query::NoSolution;
|
||||||
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
|
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
|
||||||
use rustc_middle::traits::specialization_graph::OverlapMode;
|
use rustc_middle::traits::specialization_graph::OverlapMode;
|
||||||
|
@ -35,6 +35,8 @@ use rustc_span::DUMMY_SP;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use super::error_reporting::suggest_new_overflow_limit;
|
||||||
|
|
||||||
/// Whether we do the orphan check relative to this crate or
|
/// Whether we do the orphan check relative to this crate or
|
||||||
/// to some remote crate.
|
/// to some remote crate.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -56,6 +58,9 @@ pub struct OverlapResult<'tcx> {
|
||||||
/// `true` if the overlap might've been permitted before the shift
|
/// `true` if the overlap might've been permitted before the shift
|
||||||
/// to universes.
|
/// to universes.
|
||||||
pub involves_placeholder: bool,
|
pub involves_placeholder: bool,
|
||||||
|
|
||||||
|
/// Used in the new solver to suggest increasing the recursion limit.
|
||||||
|
pub overflowing_predicates: Vec<ty::Predicate<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) {
|
pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) {
|
||||||
|
@ -65,6 +70,18 @@ pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
err: &mut Diag<'_, G>,
|
||||||
|
overflowing_predicates: &[ty::Predicate<'tcx>],
|
||||||
|
) {
|
||||||
|
for pred in overflowing_predicates {
|
||||||
|
err.note(format!("overflow evaluating the requirement `{}`", pred));
|
||||||
|
}
|
||||||
|
|
||||||
|
suggest_new_overflow_limit(tcx, err);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum TrackAmbiguityCauses {
|
enum TrackAmbiguityCauses {
|
||||||
Yes,
|
Yes,
|
||||||
|
@ -221,11 +238,13 @@ fn overlap<'tcx>(
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut overflowing_predicates = Vec::new();
|
||||||
if overlap_mode.use_implicit_negative() {
|
if overlap_mode.use_implicit_negative() {
|
||||||
if let Some(_failing_obligation) =
|
match impl_intersection_has_impossible_obligation(selcx, &obligations) {
|
||||||
impl_intersection_has_impossible_obligation(selcx, &obligations)
|
IntersectionHasImpossibleObligations::Yes => return None,
|
||||||
{
|
IntersectionHasImpossibleObligations::No { overflowing_predicates: p } => {
|
||||||
return None;
|
overflowing_predicates = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +280,12 @@ fn overlap<'tcx>(
|
||||||
impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header);
|
impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
|
Some(OverlapResult {
|
||||||
|
impl_header,
|
||||||
|
intercrate_ambiguity_causes,
|
||||||
|
involves_placeholder,
|
||||||
|
overflowing_predicates,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(infcx), ret)]
|
#[instrument(level = "debug", skip(infcx), ret)]
|
||||||
|
@ -287,6 +311,19 @@ fn equate_impl_headers<'tcx>(
|
||||||
result.map(|infer_ok| infer_ok.obligations).ok()
|
result.map(|infer_ok| infer_ok.obligations).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The result of [fn impl_intersection_has_impossible_obligation].
|
||||||
|
enum IntersectionHasImpossibleObligations<'tcx> {
|
||||||
|
Yes,
|
||||||
|
No {
|
||||||
|
/// With `-Znext-solver=coherence`, some obligations may
|
||||||
|
/// fail if only the user increased the recursion limit.
|
||||||
|
///
|
||||||
|
/// We return those obligations here and mention them in the
|
||||||
|
/// error message.
|
||||||
|
overflowing_predicates: Vec<ty::Predicate<'tcx>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
/// Check if both impls can be satisfied by a common type by considering whether
|
/// Check if both impls can be satisfied by a common type by considering whether
|
||||||
/// any of either impl's obligations is not known to hold.
|
/// any of either impl's obligations is not known to hold.
|
||||||
///
|
///
|
||||||
|
@ -308,7 +345,7 @@ fn equate_impl_headers<'tcx>(
|
||||||
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
|
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
|
||||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||||
obligations: &'a [PredicateObligation<'tcx>],
|
obligations: &'a [PredicateObligation<'tcx>],
|
||||||
) -> Option<PredicateObligation<'tcx>> {
|
) -> IntersectionHasImpossibleObligations<'tcx> {
|
||||||
let infcx = selcx.infcx;
|
let infcx = selcx.infcx;
|
||||||
|
|
||||||
if infcx.next_trait_solver() {
|
if infcx.next_trait_solver() {
|
||||||
|
@ -317,28 +354,42 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
|
||||||
|
|
||||||
// We only care about the obligations that are *definitely* true errors.
|
// We only care about the obligations that are *definitely* true errors.
|
||||||
// Ambiguities do not prove the disjointness of two impls.
|
// Ambiguities do not prove the disjointness of two impls.
|
||||||
let mut errors = fulfill_cx.select_where_possible(infcx);
|
let errors = fulfill_cx.select_where_possible(infcx);
|
||||||
errors.pop().map(|err| err.obligation)
|
if errors.is_empty() {
|
||||||
|
let overflow_errors = fulfill_cx.collect_remaining_errors(infcx);
|
||||||
|
let overflowing_predicates = overflow_errors
|
||||||
|
.into_iter()
|
||||||
|
.filter(|e| match e.code {
|
||||||
|
FulfillmentErrorCode::Ambiguity { overflow: Some(true) } => true,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate))
|
||||||
|
.collect();
|
||||||
|
IntersectionHasImpossibleObligations::No { overflowing_predicates }
|
||||||
} else {
|
} else {
|
||||||
obligations
|
IntersectionHasImpossibleObligations::Yes
|
||||||
.iter()
|
}
|
||||||
.find(|obligation| {
|
} else {
|
||||||
|
for obligation in obligations {
|
||||||
// We use `evaluate_root_obligation` to correctly track intercrate
|
// We use `evaluate_root_obligation` to correctly track intercrate
|
||||||
// ambiguity clauses. We cannot use this in the new solver.
|
// ambiguity clauses.
|
||||||
let evaluation_result = selcx.evaluate_root_obligation(obligation);
|
let evaluation_result = selcx.evaluate_root_obligation(obligation);
|
||||||
|
|
||||||
match evaluation_result {
|
match evaluation_result {
|
||||||
Ok(result) => !result.may_apply(),
|
Ok(result) => {
|
||||||
|
if !result.may_apply() {
|
||||||
|
return IntersectionHasImpossibleObligations::Yes;
|
||||||
|
}
|
||||||
|
}
|
||||||
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
|
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
|
||||||
// since there can be instantiations of this goal that don't overflow and result in
|
// since there can be instantiations of this goal that don't overflow and result in
|
||||||
// success. This isn't much of a problem in the old solver, since we treat overflow
|
// success. While this isn't much of a problem in the old solver, since we treat overflow
|
||||||
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
|
// fatally, this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>.
|
||||||
// but in the new solver, this is very important for correctness, since overflow
|
Err(_overflow) => {}
|
||||||
// *must* be treated as ambiguity for completeness.
|
|
||||||
Err(_overflow) => false,
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.cloned()
|
|
||||||
|
IntersectionHasImpossibleObligations::No { overflowing_predicates: Vec::new() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,9 @@ use crate::traits::{
|
||||||
SelectionError, SignatureMismatch, TraitNotObjectSafe,
|
SelectionError, SignatureMismatch, TraitNotObjectSafe,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
||||||
use rustc_errors::{
|
use rustc_errors::codes::*;
|
||||||
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
|
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
|
||||||
MultiSpan, StashKey, StringPart,
|
use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey};
|
||||||
};
|
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{DefKind, Namespace, Res};
|
use rustc_hir::def::{DefKind, Namespace, Res};
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||||
|
@ -62,6 +61,22 @@ pub enum OverflowCause<'tcx> {
|
||||||
TraitSolver(ty::Predicate<'tcx>),
|
TraitSolver(ty::Predicate<'tcx>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
err: &mut Diag<'_, G>,
|
||||||
|
) {
|
||||||
|
let suggested_limit = match tcx.recursion_limit() {
|
||||||
|
Limit(0) => Limit(2),
|
||||||
|
limit => limit * 2,
|
||||||
|
};
|
||||||
|
err.help(format!(
|
||||||
|
"consider increasing the recursion limit by adding a \
|
||||||
|
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
|
||||||
|
suggested_limit,
|
||||||
|
tcx.crate_name(LOCAL_CRATE),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
|
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
|
||||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
fn report_fulfillment_errors(
|
fn report_fulfillment_errors(
|
||||||
|
@ -263,7 +278,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if suggest_increasing_limit {
|
if suggest_increasing_limit {
|
||||||
self.suggest_new_overflow_limit(&mut err);
|
suggest_new_overflow_limit(self.tcx, &mut err);
|
||||||
}
|
}
|
||||||
|
|
||||||
err
|
err
|
||||||
|
@ -303,19 +318,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_new_overflow_limit(&self, err: &mut Diag<'_>) {
|
|
||||||
let suggested_limit = match self.tcx.recursion_limit() {
|
|
||||||
Limit(0) => Limit(2),
|
|
||||||
limit => limit * 2,
|
|
||||||
};
|
|
||||||
err.help(format!(
|
|
||||||
"consider increasing the recursion limit by adding a \
|
|
||||||
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
|
|
||||||
suggested_limit,
|
|
||||||
self.tcx.crate_name(LOCAL_CRATE),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reports that a cycle was detected which led to overflow and halts
|
/// Reports that a cycle was detected which led to overflow and halts
|
||||||
/// compilation. This is equivalent to `report_overflow_obligation` except
|
/// compilation. This is equivalent to `report_overflow_obligation` except
|
||||||
/// that we can give a more helpful error message (and, in particular,
|
/// that we can give a more helpful error message (and, in particular,
|
||||||
|
@ -335,12 +337,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed {
|
fn report_overflow_no_abort(
|
||||||
|
&self,
|
||||||
|
obligation: PredicateObligation<'tcx>,
|
||||||
|
suggest_increasing_limit: bool,
|
||||||
|
) -> ErrorGuaranteed {
|
||||||
let obligation = self.resolve_vars_if_possible(obligation);
|
let obligation = self.resolve_vars_if_possible(obligation);
|
||||||
let mut err = self.build_overflow_error(
|
let mut err = self.build_overflow_error(
|
||||||
OverflowCause::TraitSolver(obligation.predicate),
|
OverflowCause::TraitSolver(obligation.predicate),
|
||||||
obligation.cause.span,
|
obligation.cause.span,
|
||||||
true,
|
suggest_increasing_limit,
|
||||||
);
|
);
|
||||||
self.note_obligation_cause(&mut err, &obligation);
|
self.note_obligation_cause(&mut err, &obligation);
|
||||||
self.point_at_returns_when_relevant(&mut err, &obligation);
|
self.point_at_returns_when_relevant(&mut err, &obligation);
|
||||||
|
@ -1422,11 +1428,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||||
FulfillmentErrorCode::ProjectionError(ref e) => {
|
FulfillmentErrorCode::ProjectionError(ref e) => {
|
||||||
self.report_projection_error(&error.obligation, e)
|
self.report_projection_error(&error.obligation, e)
|
||||||
}
|
}
|
||||||
FulfillmentErrorCode::Ambiguity { overflow: false } => {
|
FulfillmentErrorCode::Ambiguity { overflow: None } => {
|
||||||
self.maybe_report_ambiguity(&error.obligation)
|
self.maybe_report_ambiguity(&error.obligation)
|
||||||
}
|
}
|
||||||
FulfillmentErrorCode::Ambiguity { overflow: true } => {
|
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
|
||||||
self.report_overflow_no_abort(error.obligation.clone())
|
self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
|
||||||
}
|
}
|
||||||
FulfillmentErrorCode::SubtypeError(ref expected_found, ref err) => self
|
FulfillmentErrorCode::SubtypeError(ref expected_found, ref err) => self
|
||||||
.report_mismatched_types(
|
.report_mismatched_types(
|
||||||
|
|
|
@ -138,7 +138,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
|
||||||
_infcx: &InferCtxt<'tcx>,
|
_infcx: &InferCtxt<'tcx>,
|
||||||
) -> Vec<FulfillmentError<'tcx>> {
|
) -> Vec<FulfillmentError<'tcx>> {
|
||||||
self.predicates
|
self.predicates
|
||||||
.to_errors(FulfillmentErrorCode::Ambiguity { overflow: false })
|
.to_errors(FulfillmentErrorCode::Ambiguity { overflow: None })
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(to_fulfillment_error)
|
.map(to_fulfillment_error)
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub struct OverlapError<'tcx> {
|
||||||
pub self_ty: Option<Ty<'tcx>>,
|
pub self_ty: Option<Ty<'tcx>>,
|
||||||
pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
|
pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
|
||||||
pub involves_placeholder: bool,
|
pub involves_placeholder: bool,
|
||||||
|
pub overflowing_predicates: Vec<ty::Predicate<'tcx>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given the generic parameters for the requested impl, translate it to the generic parameters
|
/// Given the generic parameters for the requested impl, translate it to the generic parameters
|
||||||
|
@ -435,6 +436,14 @@ fn report_conflicting_impls<'tcx>(
|
||||||
if overlap.involves_placeholder {
|
if overlap.involves_placeholder {
|
||||||
coherence::add_placeholder_note(err);
|
coherence::add_placeholder_note(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !overlap.overflowing_predicates.is_empty() {
|
||||||
|
coherence::suggest_increasing_recursion_limit(
|
||||||
|
tcx,
|
||||||
|
err,
|
||||||
|
&overlap.overflowing_predicates,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let msg = DelayDm(|| {
|
let msg = DelayDm(|| {
|
||||||
|
|
|
@ -103,6 +103,7 @@ impl<'tcx> Children {
|
||||||
self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
|
self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
|
||||||
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
|
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
|
||||||
involves_placeholder: overlap.involves_placeholder,
|
involves_placeholder: overlap.involves_placeholder,
|
||||||
|
overflowing_predicates: overlap.overflowing_predicates,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-form
|
||||||
LL | for<'a> &'a mut Self:;
|
LL | for<'a> &'a mut Self:;
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_95230`)
|
|
||||||
note: required by a bound in `Bar`
|
note: required by a bound in `Bar`
|
||||||
--> $DIR/issue-95230.rs:9:13
|
--> $DIR/issue-95230.rs:9:13
|
||||||
|
|
|
|
||||||
|
|
|
@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `Loop == _`
|
||||||
|
|
|
|
||||||
LL | impl Loop {}
|
LL | impl Loop {}
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_impls_overflow`)
|
|
||||||
|
|
||||||
error[E0392]: type parameter `T` is never used
|
error[E0392]: type parameter `T` is never used
|
||||||
--> $DIR/inherent-impls-overflow.rs:13:12
|
--> $DIR/inherent-impls-overflow.rs:13:12
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `String: Copy`
|
||||||
LL | type Item = String where String: Copy;
|
LL | type Item = String where String: Copy;
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
note: the requirement `String: Copy` appears on the `impl`'s associated type `Item` but not on the corresponding trait's associated type
|
note: the requirement `String: Copy` appears on the `impl`'s associated type `Item` but not on the corresponding trait's associated type
|
||||||
--> $DIR/alias-bound-unsound.rs:8:10
|
--> $DIR/alias-bound-unsound.rs:8:10
|
||||||
|
|
|
|
||||||
|
@ -18,32 +17,24 @@ error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item`
|
||||||
|
|
|
|
||||||
LL | drop(<() as Foo>::copy_me(&x));
|
LL | drop(<() as Foo>::copy_me(&x));
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
|
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
|
||||||
--> $DIR/alias-bound-unsound.rs:24:10
|
--> $DIR/alias-bound-unsound.rs:24:10
|
||||||
|
|
|
|
||||||
LL | drop(<() as Foo>::copy_me(&x));
|
LL | drop(<() as Foo>::copy_me(&x));
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed`
|
error[E0275]: overflow evaluating the requirement `&<() as Foo>::Item well-formed`
|
||||||
--> $DIR/alias-bound-unsound.rs:24:31
|
--> $DIR/alias-bound-unsound.rs:24:31
|
||||||
|
|
|
|
||||||
LL | drop(<() as Foo>::copy_me(&x));
|
LL | drop(<() as Foo>::copy_me(&x));
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed`
|
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item well-formed`
|
||||||
--> $DIR/alias-bound-unsound.rs:24:10
|
--> $DIR/alias-bound-unsound.rs:24:10
|
||||||
|
|
|
|
||||||
LL | drop(<() as Foo>::copy_me(&x));
|
LL | drop(<() as Foo>::copy_me(&x));
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
|
error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
|
||||||
--> $DIR/alias-bound-unsound.rs:24:10
|
--> $DIR/alias-bound-unsound.rs:24:10
|
||||||
|
@ -51,7 +42,6 @@ error[E0275]: overflow evaluating the requirement `<() as Foo>::Item == _`
|
||||||
LL | drop(<() as Foo>::copy_me(&x));
|
LL | drop(<() as Foo>::copy_me(&x));
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`)
|
|
||||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
error: aborting due to 6 previous errors
|
error: aborting due to 6 previous errors
|
||||||
|
|
|
@ -5,6 +5,9 @@ LL | impl<T: ?Sized + TwoW> Trait for W<T> {}
|
||||||
| ------------------------------------- first implementation here
|
| ------------------------------------- first implementation here
|
||||||
LL | impl<T: ?Sized + TwoW> Trait for T {}
|
LL | impl<T: ?Sized + TwoW> Trait for T {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
|
||||||
|
|
|
||||||
|
= note: overflow evaluating the requirement `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>: TwoW`
|
||||||
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`coherence_fulfill_overflow`)
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ LL | impl<T: Copy> Trait for T {}
|
||||||
LL | struct LocalTy;
|
LL | struct LocalTy;
|
||||||
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
|
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
|
||||||
|
|
|
||||||
|
= note: overflow evaluating the requirement `_ == <LocalTy as Overflow>::Assoc`
|
||||||
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`trait_ref_is_knowable_norm_overflow`)
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc: Sized`
|
error[E0275]: overflow evaluating the requirement `<T as Overflow>::Assoc: Sized`
|
||||||
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
|
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
|
||||||
LL | impls::<W<_>>();
|
LL | impls::<W<_>>();
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`)
|
|
||||||
note: required by a bound in `impls`
|
note: required by a bound in `impls`
|
||||||
--> $DIR/fixpoint-exponential-growth.rs:30:13
|
--> $DIR/fixpoint-exponential-growth.rs:30:13
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): Trait`
|
||||||
LL | impls_trait::<()>();
|
LL | impls_trait::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`)
|
|
||||||
note: required by a bound in `impls_trait`
|
note: required by a bound in `impls_trait`
|
||||||
--> $DIR/double-cycle-inductive-coinductive.rs:17:19
|
--> $DIR/double-cycle-inductive-coinductive.rs:17:19
|
||||||
|
|
|
|
||||||
|
@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): TraitRev`
|
||||||
LL | impls_trait_rev::<()>();
|
LL | impls_trait_rev::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`)
|
|
||||||
note: required by a bound in `impls_trait_rev`
|
note: required by a bound in `impls_trait_rev`
|
||||||
--> $DIR/double-cycle-inductive-coinductive.rs:29:23
|
--> $DIR/double-cycle-inductive-coinductive.rs:29:23
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
|
||||||
LL | impls_trait::<W<_>>();
|
LL | impls_trait::<W<_>>();
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_fixpoint_hang`)
|
|
||||||
note: required by a bound in `impls_trait`
|
note: required by a bound in `impls_trait`
|
||||||
--> $DIR/inductive-fixpoint-hang.rs:28:19
|
--> $DIR/inductive-fixpoint-hang.rs:28:19
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A`
|
||||||
LL | impls_a::<()>();
|
LL | impls_a::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`)
|
|
||||||
note: required by a bound in `impls_a`
|
note: required by a bound in `impls_a`
|
||||||
--> $DIR/inductive-not-on-stack.rs:25:15
|
--> $DIR/inductive-not-on-stack.rs:25:15
|
||||||
|
|
|
|
||||||
|
@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): AR`
|
||||||
LL | impls_ar::<()>();
|
LL | impls_ar::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`)
|
|
||||||
note: required by a bound in `impls_ar`
|
note: required by a bound in `impls_ar`
|
||||||
--> $DIR/inductive-not-on-stack.rs:38:16
|
--> $DIR/inductive-not-on-stack.rs:38:16
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A`
|
||||||
LL | impls_a::<()>();
|
LL | impls_a::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_1`)
|
|
||||||
note: required by a bound in `impls_a`
|
note: required by a bound in `impls_a`
|
||||||
--> $DIR/mixed-cycles-1.rs:34:15
|
--> $DIR/mixed-cycles-1.rs:34:15
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): A`
|
||||||
LL | impls_a::<()>();
|
LL | impls_a::<()>();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`mixed_cycles_2`)
|
|
||||||
note: required by a bound in `impls_a`
|
note: required by a bound in `impls_a`
|
||||||
--> $DIR/mixed-cycles-2.rs:27:15
|
--> $DIR/mixed-cycles-2.rs:27:15
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
|
||||||
LL | Self::Assoc: A<T>,
|
LL | Self::Assoc: A<T>,
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
|
|
||||||
note: the requirement `<() as A<T>>::Assoc: A<T>` appears on the `impl`'s method `f` but not on the corresponding trait's method
|
note: the requirement `<() as A<T>>::Assoc: A<T>` appears on the `impl`'s method `f` but not on the corresponding trait's method
|
||||||
--> $DIR/normalize-param-env-2.rs:12:8
|
--> $DIR/normalize-param-env-2.rs:12:8
|
||||||
|
|
|
|
||||||
|
@ -19,24 +18,18 @@ error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
|
||||||
|
|
|
|
||||||
LL | Self::Assoc: A<T>,
|
LL | Self::Assoc: A<T>,
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc well-formed`
|
error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc well-formed`
|
||||||
--> $DIR/normalize-param-env-2.rs:24:22
|
--> $DIR/normalize-param-env-2.rs:24:22
|
||||||
|
|
|
|
||||||
LL | Self::Assoc: A<T>,
|
LL | Self::Assoc: A<T>,
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `(): A<T>`
|
error[E0275]: overflow evaluating the requirement `(): A<T>`
|
||||||
--> $DIR/normalize-param-env-2.rs:27:10
|
--> $DIR/normalize-param-env-2.rs:27:10
|
||||||
|
|
|
|
||||||
LL | <() as A<T>>::f();
|
LL | <() as A<T>>::f();
|
||||||
| ^^
|
| ^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
|
error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
|
||||||
--> $DIR/normalize-param-env-2.rs:27:9
|
--> $DIR/normalize-param-env-2.rs:27:9
|
||||||
|
@ -44,7 +37,6 @@ error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
|
||||||
LL | <() as A<T>>::f();
|
LL | <() as A<T>>::f();
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
|
|
||||||
note: required by a bound in `A::f`
|
note: required by a bound in `A::f`
|
||||||
--> $DIR/normalize-param-env-2.rs:14:22
|
--> $DIR/normalize-param-env-2.rs:14:22
|
||||||
|
|
|
|
||||||
|
|
|
@ -3,16 +3,12 @@ error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc: Trait`
|
||||||
|
|
|
|
||||||
LL | <T as Trait>::Assoc: Trait,
|
LL | <T as Trait>::Assoc: Trait,
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc well-formed`
|
error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc well-formed`
|
||||||
--> $DIR/normalize-param-env-4.rs:18:26
|
--> $DIR/normalize-param-env-4.rs:18:26
|
||||||
|
|
|
|
||||||
LL | <T as Trait>::Assoc: Trait,
|
LL | <T as Trait>::Assoc: Trait,
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `T: Trait`
|
error[E0275]: overflow evaluating the requirement `T: Trait`
|
||||||
--> $DIR/normalize-param-env-4.rs:31:19
|
--> $DIR/normalize-param-env-4.rs:31:19
|
||||||
|
@ -20,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `T: Trait`
|
||||||
LL | impls_trait::<T>();
|
LL | impls_trait::<T>();
|
||||||
| ^
|
| ^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`)
|
|
||||||
note: required by a bound in `impls_trait`
|
note: required by a bound in `impls_trait`
|
||||||
--> $DIR/normalize-param-env-4.rs:14:19
|
--> $DIR/normalize-param-env-4.rs:14:19
|
||||||
|
|
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
|
||||||
LL | impls::<W<_>>();
|
LL | impls::<W<_>>();
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`exponential_trait_goals`)
|
|
||||||
note: required by a bound in `impls`
|
note: required by a bound in `impls`
|
||||||
--> $DIR/exponential-trait-goals.rs:14:13
|
--> $DIR/exponential-trait-goals.rs:14:13
|
||||||
|
|
|
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
//~ ERROR overflow evaluating the requirement `Self well-formed`
|
|
||||||
//~| ERROR overflow evaluating the requirement `Self: Trait`
|
|
||||||
|
|
||||||
// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.
|
// This is a non-regression test for issue #115351, where a recursion limit of 0 caused an ICE.
|
||||||
//@ compile-flags: -Znext-solver --crate-type=lib
|
//@ compile-flags: -Znext-solver --crate-type=lib
|
||||||
//@ check-fail
|
//@ check-pass
|
||||||
|
|
||||||
#![recursion_limit = "0"]
|
#![recursion_limit = "0"]
|
||||||
trait Trait {}
|
trait Trait {}
|
||||||
impl Trait for u32 {}
|
impl Trait for u32 {}
|
||||||
//~^ ERROR overflow evaluating the requirement `u32: Trait`
|
|
||||||
//~| ERROR overflow evaluating the requirement `u32 well-formed`
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
error[E0275]: overflow evaluating the requirement `Self: Trait`
|
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `Self well-formed`
|
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `u32: Trait`
|
|
||||||
--> $DIR/recursion-limit-zero-issue-115351.rs:10:16
|
|
||||||
|
|
|
||||||
LL | impl Trait for u32 {}
|
|
||||||
| ^^^
|
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
|
|
||||||
|
|
||||||
error[E0275]: overflow evaluating the requirement `u32 well-formed`
|
|
||||||
--> $DIR/recursion-limit-zero-issue-115351.rs:10:16
|
|
||||||
|
|
|
||||||
LL | impl Trait for u32 {}
|
|
||||||
| ^^^
|
|
||||||
|
|
|
||||||
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "2"]` attribute to your crate (`recursion_limit_zero_issue_115351`)
|
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0275`.
|
|
Loading…
Add table
Add a link
Reference in a new issue