Skip to content

-Znext-solver=coherence: suggest increasing recursion limit #121497

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions compiler/rustc_infer/src/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -135,16 +135,18 @@ 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.
/// Inherently impossible to fulfill; this trait is implemented if and only
/// if it is already implemented.
Cycle(Vec<PredicateObligation<'tcx>>),
SelectionError(SelectionError<'tcx>),
ProjectionError(MismatchedProjectionTypes<'tcx>),
SubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
ConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
Ambiguity {
/// Overflow reported from the new solver `-Znext-solver`, which will
/// be reported as an regular error as opposed to a fatal error.
overflow: bool,
/// Overflow is only `Some(suggest_recursion_limit)` when using the next generation
/// trait solver `-Znext-solver`. With the old solver overflow is eagerly handled by
/// emitting a fatal error instead.
overflow: Option<bool>,
},
}

6 changes: 4 additions & 2 deletions compiler/rustc_infer/src/traits/structural_impls.rs
Original file line number Diff line number Diff line change
@@ -47,8 +47,10 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
ConstEquateError(ref a, ref b) => {
write!(f, "CodeConstEquateError({a:?}, {b:?})")
}
Ambiguity { overflow: false } => write!(f, "Ambiguity"),
Ambiguity { overflow: true } => write!(f, "Overflow"),
Ambiguity { overflow: None } => write!(f, "Ambiguity"),
Ambiguity { overflow: Some(suggest_increasing_limit) } => {
write!(f, "Overflow({suggest_increasing_limit})")
}
Cycle(ref cycle) => write!(f, "Cycle({cycle:?})"),
}
}
30 changes: 20 additions & 10 deletions compiler/rustc_middle/src/traits/solve.rs
Original file line number Diff line number Diff line change
@@ -60,7 +60,6 @@ pub enum Certainty {

impl Certainty {
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.
///
@@ -79,16 +78,13 @@ impl Certainty {
(Certainty::Yes, Certainty::Yes) => Certainty::Yes,
(Certainty::Yes, Certainty::Maybe(_)) => other,
(Certainty::Maybe(_), Certainty::Yes) => self,
(Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(MaybeCause::Ambiguity)) => {
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)
}
(Certainty::Maybe(a), Certainty::Maybe(b)) => Certainty::Maybe(a.unify_with(b)),
}
}

pub const fn overflow(suggest_increasing_limit: bool) -> Certainty {
Certainty::Maybe(MaybeCause::Overflow { suggest_increasing_limit })
}
}

/// Why we failed to evaluate a goal.
@@ -99,7 +95,21 @@ pub enum MaybeCause {
/// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
Ambiguity,
/// 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 },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't suggest increasing the limit if we hit a true cycle, correct? I feel like this should be &&, because increasing the limit here when one of the branches is false (i.e. cycle) will not have a positive effect.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or hitting the fixpoint limit, which also won't be improved by increasing the limit.

Copy link
Contributor Author

@lcnr lcnr Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will, in case one of the goals fails or has no constraints, if we have two nested goals:

  • non recursion_limit cycle
  • recursion_limit increase -> error

then we should suggest increasing the recursion limit as it would change the result to NoSolution which may then remove ambiguity elsewhere

}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, HashStable, TypeFoldable, TypeVisitable)]
6 changes: 4 additions & 2 deletions compiler/rustc_trait_selection/src/solve/alias_relate.rs
Original file line number Diff line number Diff line change
@@ -36,11 +36,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;

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 {
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 {
33 changes: 16 additions & 17 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ use rustc_infer::infer::{
BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TyCtxtInferExt,
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::solve::MaybeCause;
use rustc_infer::traits::ObligationCause;
use rustc_middle::infer::canonical::CanonicalVarInfos;
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 super::inspect::ProofTreeBuilder;
use super::{search_graph, GoalEvaluationKind};
use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
use super::{search_graph::SearchGraph, Goal};
use super::{GoalSource, SolverMode};
pub use select::InferCtxtSelectExt;
@@ -154,10 +155,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
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
/// used from outside of any evaluation, and other methods should be preferred
/// 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,
) -> (R, Option<inspect::GoalEvaluation<'tcx>>) {
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 {
search_graph: &mut search_graph,
@@ -388,16 +385,18 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
&& source != GoalSource::ImplWhereBound
};

if response.value.certainty == Certainty::OVERFLOW && !keep_overflow_constraints() {
(Certainty::OVERFLOW, false)
} else {
let has_changed = !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty();

let certainty =
self.instantiate_and_apply_query_response(param_env, original_values, response);
(certainty, has_changed)
if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty
&& !keep_overflow_constraints()
{
return (response.value.certainty, false);
}

let has_changed = !response.value.var_values.is_identity_modulo_regions()
|| !response.value.external_constraints.opaque_types.is_empty();

let certainty =
self.instantiate_and_apply_query_response(param_env, original_values, response);
(certainty, has_changed)
}

fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
@@ -466,8 +465,8 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let inspect = self.inspect.new_evaluate_added_goals();
let inspect = core::mem::replace(&mut self.inspect, inspect);

let mut response = Ok(Certainty::OVERFLOW);
for _ in 0..self.local_overflow_limit() {
let mut response = Ok(Certainty::overflow(false));
for _ in 0..FIXPOINT_STEP_LIMIT {
// 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.
match self.evaluate_added_goals_step() {
237 changes: 147 additions & 90 deletions compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@ use super::{Certainty, InferCtxtEvalExt};
/// 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`.
pub struct FulfillmentCtxt<'tcx> {
obligations: Vec<PredicateObligation<'tcx>>,
obligations: ObligationStorage<'tcx>,

/// The snapshot in which this context was created. Using the context
/// outside of this snapshot leads to subtle bugs if the snapshot
@@ -33,14 +33,68 @@ pub struct FulfillmentCtxt<'tcx> {
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> {
pub fn new(infcx: &InferCtxt<'tcx>) -> FulfillmentCtxt<'tcx> {
assert!(
infcx.next_trait_solver(),
"new trait solver fulfillment context created when \
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(
@@ -67,120 +121,52 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
obligation: PredicateObligation<'tcx>,
) {
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>> {
self.obligations
let mut errors: Vec<_> = self
.obligations
.pending
.drain(..)
.map(|obligation| {
let code = infcx.probe(|_| {
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")
}
}
});
.map(|obligation| fulfillment_error_for_stalled(infcx, obligation))
.collect();

FulfillmentError {
obligation: obligation.clone(),
code,
root_obligation: obligation,
}
})
.collect()
errors.extend(self.obligations.overflowed.drain(..).map(|obligation| FulfillmentError {
root_obligation: obligation.clone(),
code: FulfillmentErrorCode::Ambiguity { overflow: Some(true) },
obligation,
}));

errors
}

fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots());
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
// Only return true errors that we have accumulated while processing;
// keep ambiguities around, *including overflows*, because they shouldn't
// be considered true errors.
self.obligations.on_fulfillment_overflow(infcx);
// Only return true errors that we have accumulated while processing.
return errors;
}

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 result = infcx.evaluate_root_goal(goal, GenerateProofTree::IfEnabled).0;
self.inspect_evaluated_obligation(infcx, &obligation, &result);
let (changed, certainty) = match result {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
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,
});
errors.push(fulfillment_error_for_no_solution(infcx, obligation));
continue;
}
};
has_changed |= changed;
match certainty {
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>> {
self.obligations.clone()
self.obligations.clone_pending()
}

fn drain_unstalled_obligations(
&mut self,
_: &InferCtxt<'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 }
}
11 changes: 11 additions & 0 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
@@ -42,6 +42,17 @@ pub use fulfill::FulfillmentCtxt;
pub(crate) use normalize::deeply_normalize_for_diagnostics;
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)]
enum SolverMode {
/// Ordinary trait solving, using everywhere except for coherence.
21 changes: 9 additions & 12 deletions compiler/rustc_trait_selection/src/solve/search_graph.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::solve::FIXPOINT_STEP_LIMIT;

use super::inspect;
use super::inspect::ProofTreeBuilder;
use super::SolverMode;
@@ -99,7 +101,6 @@ impl<'tcx> ProvisionalCacheEntry<'tcx> {

pub(super) struct SearchGraph<'tcx> {
mode: SolverMode,
local_overflow_limit: usize,
/// The stack of goals currently being computed.
///
/// 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> {
pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> {
pub(super) fn new(mode: SolverMode) -> SearchGraph<'tcx> {
Self {
mode,
local_overflow_limit: tcx.recursion_limit().0.checked_ilog2().unwrap_or(0) as usize,
stack: Default::default(),
provisional_cache: Default::default(),
cycle_participants: Default::default(),
@@ -130,10 +130,6 @@ impl<'tcx> SearchGraph<'tcx> {
self.mode
}

pub(super) fn local_overflow_limit(&self) -> usize {
self.local_overflow_limit
}

/// Update the stack and reached depths on cache hits.
#[instrument(level = "debug", skip(self))]
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);
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.
@@ -370,7 +366,7 @@ impl<'tcx> SearchGraph<'tcx> {
} else if is_coinductive_cycle {
Self::response_no_constraints(tcx, input, Certainty::Yes)
} else {
Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)
Self::response_no_constraints(tcx, input, Certainty::overflow(false))
};
} else {
// 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 the previous iteration is equal to the final result, at which
// point we are done.
for _ in 0..self.local_overflow_limit() {
for _ in 0..FIXPOINT_STEP_LIMIT {
let result = prove_goal(self, inspect);
let stack_entry = self.pop_stack();
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 {
Self::response_no_constraints(tcx, input, Certainty::Yes) == result
} 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 {
false
};
@@ -452,7 +449,7 @@ impl<'tcx> SearchGraph<'tcx> {
debug!("canonical cycle overflow");
let current_entry = self.pop_stack();
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)
});

105 changes: 78 additions & 27 deletions compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ use rustc_errors::{Diag, EmissionGuarantee};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
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::solve::{CandidateSource, Certainty, Goal};
use rustc_middle::traits::specialization_graph::OverlapMode;
@@ -35,6 +35,8 @@ use rustc_span::DUMMY_SP;
use std::fmt::Debug;
use std::ops::ControlFlow;

use super::error_reporting::suggest_new_overflow_limit;

/// Whether we do the orphan check relative to this crate or
/// to some remote crate.
#[derive(Copy, Clone, Debug)]
@@ -56,6 +58,9 @@ pub struct OverlapResult<'tcx> {
/// `true` if the overlap might've been permitted before the shift
/// to universes.
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>) {
@@ -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)]
enum TrackAmbiguityCauses {
Yes,
@@ -221,11 +238,13 @@ fn overlap<'tcx>(
),
);

let mut overflowing_predicates = Vec::new();
if overlap_mode.use_implicit_negative() {
if let Some(_failing_obligation) =
impl_intersection_has_impossible_obligation(selcx, &obligations)
{
return None;
match impl_intersection_has_impossible_obligation(selcx, &obligations) {
IntersectionHasImpossibleObligations::Yes => return None,
IntersectionHasImpossibleObligations::No { overflowing_predicates: p } => {
overflowing_predicates = p
}
}
}

@@ -261,7 +280,12 @@ fn overlap<'tcx>(
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)]
@@ -287,6 +311,19 @@ fn equate_impl_headers<'tcx>(
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
/// 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>(
selcx: &mut SelectionContext<'cx, 'tcx>,
obligations: &'a [PredicateObligation<'tcx>],
) -> Option<PredicateObligation<'tcx>> {
) -> IntersectionHasImpossibleObligations<'tcx> {
let infcx = selcx.infcx;

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.
// Ambiguities do not prove the disjointness of two impls.
let mut errors = fulfill_cx.select_where_possible(infcx);
errors.pop().map(|err| err.obligation)
let errors = fulfill_cx.select_where_possible(infcx);
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 {
IntersectionHasImpossibleObligations::Yes
}
} else {
obligations
.iter()
.find(|obligation| {
// We use `evaluate_root_obligation` to correctly track intercrate
// ambiguity clauses. We cannot use this in the new solver.
let evaluation_result = selcx.evaluate_root_obligation(obligation);

match evaluation_result {
Ok(result) => !result.may_apply(),
// 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
// success. 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>),
// but in the new solver, this is very important for correctness, since overflow
// *must* be treated as ambiguity for completeness.
Err(_overflow) => false,
for obligation in obligations {
// We use `evaluate_root_obligation` to correctly track intercrate
// ambiguity clauses.
let evaluation_result = selcx.evaluate_root_obligation(obligation);

match evaluation_result {
Ok(result) => {
if !result.may_apply() {
return IntersectionHasImpossibleObligations::Yes;
}
}
})
.cloned()
// 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
// 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>.
Err(_overflow) => {}
}
}

IntersectionHasImpossibleObligations::No { overflowing_predicates: Vec::new() }
}
}

Original file line number Diff line number Diff line change
@@ -20,10 +20,9 @@ use crate::traits::{
SelectionError, SignatureMismatch, TraitNotObjectSafe,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
MultiSpan, StashKey, StringPart,
};
use rustc_errors::codes::*;
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace, Res};
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -62,6 +61,22 @@ pub enum OverflowCause<'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>)]
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn report_fulfillment_errors(
@@ -263,7 +278,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};

if suggest_increasing_limit {
self.suggest_new_overflow_limit(&mut err);
suggest_new_overflow_limit(self.tcx, &mut 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
/// compilation. This is equivalent to `report_overflow_obligation` except
/// 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 mut err = self.build_overflow_error(
OverflowCause::TraitSolver(obligation.predicate),
obligation.cause.span,
true,
suggest_increasing_limit,
);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
@@ -1422,11 +1428,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
FulfillmentErrorCode::ProjectionError(ref e) => {
self.report_projection_error(&error.obligation, e)
}
FulfillmentErrorCode::Ambiguity { overflow: false } => {
FulfillmentErrorCode::Ambiguity { overflow: None } => {
self.maybe_report_ambiguity(&error.obligation)
}
FulfillmentErrorCode::Ambiguity { overflow: true } => {
self.report_overflow_no_abort(error.obligation.clone())
FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
}
FulfillmentErrorCode::SubtypeError(ref expected_found, ref err) => self
.report_mismatched_types(
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/traits/fulfill.rs
Original file line number Diff line number Diff line change
@@ -138,7 +138,7 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
_infcx: &InferCtxt<'tcx>,
) -> Vec<FulfillmentError<'tcx>> {
self.predicates
.to_errors(FulfillmentErrorCode::Ambiguity { overflow: false })
.to_errors(FulfillmentErrorCode::Ambiguity { overflow: None })
.into_iter()
.map(to_fulfillment_error)
.collect()
9 changes: 9 additions & 0 deletions compiler/rustc_trait_selection/src/traits/specialize/mod.rs
Original file line number Diff line number Diff line change
@@ -39,6 +39,7 @@ pub struct OverlapError<'tcx> {
pub self_ty: Option<Ty<'tcx>>,
pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
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
@@ -435,6 +436,14 @@ fn report_conflicting_impls<'tcx>(
if overlap.involves_placeholder {
coherence::add_placeholder_note(err);
}

if !overlap.overflowing_predicates.is_empty() {
coherence::suggest_increasing_recursion_limit(
tcx,
err,
&overlap.overflowing_predicates,
);
}
}

let msg = DelayDm(|| {
Original file line number Diff line number Diff line change
@@ -103,6 +103,7 @@ impl<'tcx> Children {
self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
involves_placeholder: overlap.involves_placeholder,
overflowing_predicates: overlap.overflowing_predicates,
}
};

Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-form
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`
--> $DIR/issue-95230.rs:9:13
|
2 changes: 0 additions & 2 deletions tests/ui/lazy-type-alias/inherent-impls-overflow.next.stderr
Original file line number Diff line number Diff line change
@@ -3,8 +3,6 @@ error[E0275]: overflow evaluating the requirement `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
--> $DIR/inherent-impls-overflow.rs:13:12
10 changes: 0 additions & 10 deletions tests/ui/traits/next-solver/alias-bound-unsound.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `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
--> $DIR/alias-bound-unsound.rs:8:10
|
@@ -18,40 +17,31 @@ error[E0275]: overflow evaluating the requirement `String <: <() as Foo>::Item`
|
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 == _`
--> $DIR/alias-bound-unsound.rs:24:10
|
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`
--> $DIR/alias-bound-unsound.rs:24:31
|
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`
--> $DIR/alias-bound-unsound.rs:24:10
|
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 == _`
--> $DIR/alias-bound-unsound.rs:24:10
|
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`

error: aborting due to 6 previous errors
3 changes: 3 additions & 0 deletions tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr
Original file line number Diff line number Diff line change
@@ -5,6 +5,9 @@ LL | impl<T: ?Sized + TwoW> Trait for W<T> {}
| ------------------------------------- first implementation here
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<_>>>>>>>>>>>>>>>>>>>>>`
|
= 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

Original file line number Diff line number Diff line change
@@ -6,6 +6,9 @@ LL | impl<T: Copy> Trait for T {}
LL | struct LocalTy;
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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`
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:10:18
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
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`
--> $DIR/fixpoint-exponential-growth.rs:30:13
|
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): 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`
--> $DIR/double-cycle-inductive-coinductive.rs:17:19
|
@@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): TraitRev`
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`
--> $DIR/double-cycle-inductive-coinductive.rs:29:23
|
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
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`
--> $DIR/inductive-fixpoint-hang.rs:28:19
|
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): 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`
--> $DIR/inductive-not-on-stack.rs:25:15
|
@@ -17,7 +16,6 @@ error[E0275]: overflow evaluating the requirement `(): 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`
--> $DIR/inductive-not-on-stack.rs:38:16
|
1 change: 0 additions & 1 deletion tests/ui/traits/next-solver/cycles/mixed-cycles-1.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): 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`
--> $DIR/mixed-cycles-1.rs:34:15
|
1 change: 0 additions & 1 deletion tests/ui/traits/next-solver/cycles/mixed-cycles-2.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `(): 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`
--> $DIR/mixed-cycles-2.rs:27:15
|
8 changes: 0 additions & 8 deletions tests/ui/traits/next-solver/normalize-param-env-2.stderr
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `<() as A<T>>::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
--> $DIR/normalize-param-env-2.rs:12:8
|
@@ -19,32 +18,25 @@ error[E0275]: overflow evaluating the requirement `<() as A<T>>::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`
--> $DIR/normalize-param-env-2.rs:24:22
|
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>`
--> $DIR/normalize-param-env-2.rs:27:10
|
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>`
--> $DIR/normalize-param-env-2.rs:27:9
|
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`
--> $DIR/normalize-param-env-2.rs:14:22
|
5 changes: 0 additions & 5 deletions tests/ui/traits/next-solver/normalize-param-env-4.next.stderr
Original file line number Diff line number Diff line change
@@ -3,24 +3,19 @@ error[E0275]: overflow evaluating the requirement `<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`
--> $DIR/normalize-param-env-4.rs:18:26
|
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`
--> $DIR/normalize-param-env-4.rs:31:19
|
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`
--> $DIR/normalize-param-env-4.rs:14:19
|
Original file line number Diff line number Diff line change
@@ -4,7 +4,6 @@ error[E0275]: overflow evaluating the requirement `W<_>: Trait`
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`
--> $DIR/exponential-trait-goals.rs:14:13
|
Original file line number Diff line number Diff line change
@@ -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.
//@ compile-flags: -Znext-solver --crate-type=lib
//@ check-fail
//@ check-pass

#![recursion_limit = "0"]
trait Trait {}
impl Trait for u32 {}
//~^ ERROR overflow evaluating the requirement `u32: Trait`
//~| ERROR overflow evaluating the requirement `u32 well-formed`

This file was deleted.