Skip to content

Commit eeba944

Browse files
committed
track overflowing goals for overfow errors
1 parent bdb0a78 commit eeba944

File tree

5 files changed

+99
-43
lines changed

5 files changed

+99
-43
lines changed

compiler/rustc_trait_selection/src/traits/coherence.rs

+67-27
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_errors::{DiagnosticBuilder, EmissionGuarantee};
2222
use rustc_hir::def::DefKind;
2323
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
2424
use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
25-
use rustc_infer::traits::{util, TraitEngine, TraitEngineExt};
25+
use rustc_infer::traits::{util, FulfillmentErrorCode, TraitEngine, TraitEngineExt};
2626
use rustc_middle::traits::query::NoSolution;
2727
use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal};
2828
use rustc_middle::traits::specialization_graph::OverlapMode;
@@ -35,6 +35,8 @@ use rustc_span::DUMMY_SP;
3535
use std::fmt::Debug;
3636
use std::ops::ControlFlow;
3737

38+
use super::error_reporting::suggest_new_overflow_limit;
39+
3840
/// Whether we do the orphan check relative to this crate or
3941
/// to some remote crate.
4042
#[derive(Copy, Clone, Debug)]
@@ -56,6 +58,9 @@ pub struct OverlapResult<'tcx> {
5658
/// `true` if the overlap might've been permitted before the shift
5759
/// to universes.
5860
pub involves_placeholder: bool,
61+
62+
/// Used in the new solver to suggest increasing the recursion limit.
63+
pub overflowing_predicates: Vec<ty::Predicate<'tcx>>,
5964
}
6065

6166
pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_, G>) {
@@ -65,6 +70,18 @@ pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_
6570
);
6671
}
6772

73+
pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>(
74+
tcx: TyCtxt<'tcx>,
75+
err: &mut DiagnosticBuilder<'_, G>,
76+
overflowing_predicates: &[ty::Predicate<'tcx>],
77+
) {
78+
for pred in overflowing_predicates {
79+
err.note(format!("overflow evaluating the requirement `{}`", pred));
80+
}
81+
82+
suggest_new_overflow_limit(tcx, err);
83+
}
84+
6885
#[derive(Debug, Clone, Copy)]
6986
enum TrackAmbiguityCauses {
7087
Yes,
@@ -221,11 +238,11 @@ fn overlap<'tcx>(
221238
),
222239
);
223240

241+
let mut overflowing_predicates = Vec::new();
224242
if overlap_mode.use_implicit_negative() {
225-
if let Some(_failing_obligation) =
226-
impl_intersection_has_impossible_obligation(selcx, &obligations)
227-
{
228-
return None;
243+
match impl_intersection_has_impossible_obligation(selcx, &obligations) {
244+
Ok(()) => return None,
245+
Err(p) => overflowing_predicates = p,
229246
}
230247
}
231248

@@ -261,7 +278,12 @@ fn overlap<'tcx>(
261278
impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header);
262279
}
263280

264-
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
281+
Some(OverlapResult {
282+
impl_header,
283+
intercrate_ambiguity_causes,
284+
involves_placeholder,
285+
overflowing_predicates,
286+
})
265287
}
266288

267289
#[instrument(level = "debug", skip(infcx), ret)]
@@ -305,10 +327,14 @@ fn equate_impl_headers<'tcx>(
305327
/// of the two impls above to be empty.
306328
///
307329
/// Importantly, this works even if there isn't a `impl !Error for MyLocalType`.
330+
///
331+
/// If there is no impossible obligation, this returns a list of obligations which
332+
/// overflowed by hitting the `recursion_limit` in the new solver. This is used
333+
/// to improve the error message.
308334
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
309335
selcx: &mut SelectionContext<'cx, 'tcx>,
310336
obligations: &'a [PredicateObligation<'tcx>],
311-
) -> Option<PredicateObligation<'tcx>> {
337+
) -> Result<(), Vec<ty::Predicate<'tcx>>> {
312338
let infcx = selcx.infcx;
313339

314340
if infcx.next_trait_solver() {
@@ -317,28 +343,42 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
317343

318344
// We only care about the obligations that are *definitely* true errors.
319345
// Ambiguities do not prove the disjointness of two impls.
320-
let mut errors = fulfill_cx.select_where_possible(infcx);
321-
errors.pop().map(|err| err.obligation)
346+
let errors = fulfill_cx.select_where_possible(infcx);
347+
if errors.is_empty() {
348+
let overflow_errors = fulfill_cx.collect_remaining_errors(infcx);
349+
let overflowing_predicates = overflow_errors
350+
.into_iter()
351+
.filter(|e| match e.code {
352+
FulfillmentErrorCode::Ambiguity { overflow: Some(true) } => true,
353+
_ => false,
354+
})
355+
.map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate))
356+
.collect();
357+
Err(overflowing_predicates)
358+
} else {
359+
Ok(())
360+
}
322361
} else {
323-
obligations
324-
.iter()
325-
.find(|obligation| {
326-
// We use `evaluate_root_obligation` to correctly track intercrate
327-
// ambiguity clauses. We cannot use this in the new solver.
328-
let evaluation_result = selcx.evaluate_root_obligation(obligation);
329-
330-
match evaluation_result {
331-
Ok(result) => !result.may_apply(),
332-
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
333-
// since there can be instantiations of this goal that don't overflow and result in
334-
// success. This isn't much of a problem in the old solver, since we treat overflow
335-
// fatally (this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>),
336-
// but in the new solver, this is very important for correctness, since overflow
337-
// *must* be treated as ambiguity for completeness.
338-
Err(_overflow) => false,
362+
for obligation in obligations {
363+
// We use `evaluate_root_obligation` to correctly track intercrate
364+
// ambiguity clauses.
365+
let evaluation_result = selcx.evaluate_root_obligation(obligation);
366+
367+
match evaluation_result {
368+
Ok(result) => {
369+
if !result.may_apply() {
370+
return Ok(());
371+
}
339372
}
340-
})
341-
.cloned()
373+
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
374+
// since there can be instantiations of this goal that don't overflow and result in
375+
// success. While this isn't much of a problem in the old solver, since we treat overflow
376+
// fatally, this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>.
377+
Err(_overflow) => {}
378+
}
379+
}
380+
381+
Err(Vec::new())
342382
}
343383
}
344384

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

+19-16
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ use crate::traits::{
2121
};
2222
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
2323
use rustc_errors::{
24-
codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
25-
FatalError, MultiSpan, StashKey, StringPart,
24+
codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, EmissionGuarantee,
25+
ErrorGuaranteed, FatalError, MultiSpan, StashKey, StringPart,
2626
};
2727
use rustc_hir as hir;
2828
use rustc_hir::def::{DefKind, Namespace, Res};
@@ -62,6 +62,22 @@ pub enum OverflowCause<'tcx> {
6262
TraitSolver(ty::Predicate<'tcx>),
6363
}
6464

65+
pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
66+
tcx: TyCtxt<'tcx>,
67+
err: &mut DiagnosticBuilder<'_, G>,
68+
) {
69+
let suggested_limit = match tcx.recursion_limit() {
70+
Limit(0) => Limit(2),
71+
limit => limit * 2,
72+
};
73+
err.help(format!(
74+
"consider increasing the recursion limit by adding a \
75+
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
76+
suggested_limit,
77+
tcx.crate_name(LOCAL_CRATE),
78+
));
79+
}
80+
6581
#[extension(pub trait TypeErrCtxtExt<'tcx>)]
6682
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
6783
fn report_fulfillment_errors(
@@ -263,7 +279,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
263279
};
264280

265281
if suggest_increasing_limit {
266-
self.suggest_new_overflow_limit(&mut err);
282+
suggest_new_overflow_limit(self.tcx, &mut err);
267283
}
268284

269285
err
@@ -303,19 +319,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
303319
);
304320
}
305321

306-
fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
307-
let suggested_limit = match self.tcx.recursion_limit() {
308-
Limit(0) => Limit(2),
309-
limit => limit * 2,
310-
};
311-
err.help(format!(
312-
"consider increasing the recursion limit by adding a \
313-
`#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
314-
suggested_limit,
315-
self.tcx.crate_name(LOCAL_CRATE),
316-
));
317-
}
318-
319322
/// Reports that a cycle was detected which led to overflow and halts
320323
/// compilation. This is equivalent to `report_overflow_obligation` except
321324
/// that we can give a more helpful error message (and, in particular,

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

+9
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub struct OverlapError<'tcx> {
3939
pub self_ty: Option<Ty<'tcx>>,
4040
pub intercrate_ambiguity_causes: FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
4141
pub involves_placeholder: bool,
42+
pub overflowing_predicates: Vec<ty::Predicate<'tcx>>,
4243
}
4344

4445
/// Given the generic parameters for the requested impl, translate it to the generic parameters
@@ -435,6 +436,14 @@ fn report_conflicting_impls<'tcx>(
435436
if overlap.involves_placeholder {
436437
coherence::add_placeholder_note(err);
437438
}
439+
440+
if !overlap.overflowing_predicates.is_empty() {
441+
coherence::suggest_increasing_recursion_limit(
442+
tcx,
443+
err,
444+
&overlap.overflowing_predicates,
445+
);
446+
}
438447
}
439448

440449
let msg = DelayDm(|| {

compiler/rustc_trait_selection/src/traits/specialize/specialization_graph.rs

+1
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ impl<'tcx> Children {
103103
self_ty: self_ty.has_concrete_skeleton().then_some(self_ty),
104104
intercrate_ambiguity_causes: overlap.intercrate_ambiguity_causes,
105105
involves_placeholder: overlap.involves_placeholder,
106+
overflowing_predicates: overlap.overflowing_predicates,
106107
}
107108
};
108109

tests/ui/traits/next-solver/coherence-fulfill-overflow.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ LL | impl<T: ?Sized + TwoW> Trait for W<T> {}
55
| ------------------------------------- first implementation here
66
LL | impl<T: ?Sized + TwoW> Trait for T {}
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<W<_>>>>>>>>>>>>>>>>>>>>>`
8+
|
9+
= 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`
10+
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` attribute to your crate (`coherence_fulfill_overflow`)
811

912
error: aborting due to 1 previous error
1013

0 commit comments

Comments
 (0)