Skip to content

Commit 8c5e83d

Browse files
committed
track overflowing goals for overfow errors
1 parent 3605a09 commit 8c5e83d

File tree

5 files changed

+111
-45
lines changed

5 files changed

+111
-45
lines changed

compiler/rustc_trait_selection/src/traits/coherence.rs

+78-27
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use rustc_errors::{Diag, 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 Diag<'_, G>) {
@@ -65,6 +70,18 @@ pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut Diag<'_, G>) {
6570
);
6671
}
6772

73+
pub fn suggest_increasing_recursion_limit<'tcx, G: EmissionGuarantee>(
74+
tcx: TyCtxt<'tcx>,
75+
err: &mut Diag<'_, 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,13 @@ 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+
IntersectionHasImpossibleObligations::Yes => return None,
245+
IntersectionHasImpossibleObligations::No { overflowing_predicates: p } => {
246+
overflowing_predicates = p
247+
}
229248
}
230249
}
231250

@@ -261,7 +280,12 @@ fn overlap<'tcx>(
261280
impl_header = deeply_normalize_for_diagnostics(&infcx, param_env, impl_header);
262281
}
263282

264-
Some(OverlapResult { impl_header, intercrate_ambiguity_causes, involves_placeholder })
283+
Some(OverlapResult {
284+
impl_header,
285+
intercrate_ambiguity_causes,
286+
involves_placeholder,
287+
overflowing_predicates,
288+
})
265289
}
266290

267291
#[instrument(level = "debug", skip(infcx), ret)]
@@ -287,6 +311,19 @@ fn equate_impl_headers<'tcx>(
287311
result.map(|infer_ok| infer_ok.obligations).ok()
288312
}
289313

314+
/// The result of [fn impl_intersection_has_impossible_obligation].
315+
enum IntersectionHasImpossibleObligations<'tcx> {
316+
Yes,
317+
No {
318+
/// With `-Znext-solver=coherence`, some obligations may
319+
/// fail if only the user increased the recursion limit.
320+
///
321+
/// We return those obligations here and mention them in the
322+
/// error message.
323+
overflowing_predicates: Vec<ty::Predicate<'tcx>>,
324+
},
325+
}
326+
290327
/// Check if both impls can be satisfied by a common type by considering whether
291328
/// any of either impl's obligations is not known to hold.
292329
///
@@ -308,7 +345,7 @@ fn equate_impl_headers<'tcx>(
308345
fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
309346
selcx: &mut SelectionContext<'cx, 'tcx>,
310347
obligations: &'a [PredicateObligation<'tcx>],
311-
) -> Option<PredicateObligation<'tcx>> {
348+
) -> IntersectionHasImpossibleObligations<'tcx> {
312349
let infcx = selcx.infcx;
313350

314351
if infcx.next_trait_solver() {
@@ -317,28 +354,42 @@ fn impl_intersection_has_impossible_obligation<'a, 'cx, 'tcx>(
317354

318355
// We only care about the obligations that are *definitely* true errors.
319356
// 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)
357+
let errors = fulfill_cx.select_where_possible(infcx);
358+
if errors.is_empty() {
359+
let overflow_errors = fulfill_cx.collect_remaining_errors(infcx);
360+
let overflowing_predicates = overflow_errors
361+
.into_iter()
362+
.filter(|e| match e.code {
363+
FulfillmentErrorCode::Ambiguity { overflow: Some(true) } => true,
364+
_ => false,
365+
})
366+
.map(|e| infcx.resolve_vars_if_possible(e.obligation.predicate))
367+
.collect();
368+
IntersectionHasImpossibleObligations::No { overflowing_predicates }
369+
} else {
370+
IntersectionHasImpossibleObligations::Yes
371+
}
322372
} 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,
373+
for obligation in obligations {
374+
// We use `evaluate_root_obligation` to correctly track intercrate
375+
// ambiguity clauses.
376+
let evaluation_result = selcx.evaluate_root_obligation(obligation);
377+
378+
match evaluation_result {
379+
Ok(result) => {
380+
if !result.may_apply() {
381+
return IntersectionHasImpossibleObligations::Yes;
382+
}
339383
}
340-
})
341-
.cloned()
384+
// If overflow occurs, we need to conservatively treat the goal as possibly holding,
385+
// since there can be instantiations of this goal that don't overflow and result in
386+
// success. While this isn't much of a problem in the old solver, since we treat overflow
387+
// fatally, this still can be encountered: <https://github.com/rust-lang/rust/issues/105231>.
388+
Err(_overflow) => {}
389+
}
390+
}
391+
392+
IntersectionHasImpossibleObligations::No { overflowing_predicates: Vec::new() }
342393
}
343394
}
344395

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

+20-18
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@ use crate::traits::{
2020
SelectionError, SignatureMismatch, TraitNotObjectSafe,
2121
};
2222
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
23-
use rustc_errors::{
24-
codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
25-
MultiSpan, StashKey, StringPart,
26-
};
23+
use rustc_errors::codes::*;
24+
use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
25+
use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey};
2726
use rustc_hir as hir;
2827
use rustc_hir::def::{DefKind, Namespace, Res};
2928
use rustc_hir::def_id::{DefId, LocalDefId};
@@ -62,6 +61,22 @@ pub enum OverflowCause<'tcx> {
6261
TraitSolver(ty::Predicate<'tcx>),
6362
}
6463

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

265280
if suggest_increasing_limit {
266-
self.suggest_new_overflow_limit(&mut err);
281+
suggest_new_overflow_limit(self.tcx, &mut err);
267282
}
268283

269284
err
@@ -303,19 +318,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
303318
);
304319
}
305320

306-
fn suggest_new_overflow_limit(&self, err: &mut Diag<'_>) {
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-
319321
/// Reports that a cycle was detected which led to overflow and halts
320322
/// compilation. This is equivalent to `report_overflow_obligation` except
321323
/// 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)