1
Fork 0

Report overflows gracefully with new solver

This commit is contained in:
Michael Goulet 2023-04-09 04:29:43 +00:00
parent 2a198c7f62
commit 05a6daab84
14 changed files with 75 additions and 27 deletions

View file

@ -578,7 +578,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
#[instrument(skip(self), level = "debug")] #[instrument(skip(self), level = "debug")]
pub(in super::super) fn report_ambiguity_errors(&self) { pub(in super::super) fn report_ambiguity_errors(&self) {
let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(); let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors(self);
if !errors.is_empty() { if !errors.is_empty() {
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);

View file

@ -78,7 +78,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Finally, for ambiguity-related errors, we actually want to look // Finally, for ambiguity-related errors, we actually want to look
// for a parameter that is the source of the inference type left // for a parameter that is the source of the inference type left
// over in this predicate. // over in this predicate.
if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code { if let traits::FulfillmentErrorCode::CodeAmbiguity { .. } = error.code {
fallback_param_to_point_at = None; fallback_param_to_point_at = None;
self_param_to_point_at = None; self_param_to_point_at = None;
param_to_point_at = param_to_point_at =

View file

@ -38,7 +38,7 @@ pub trait TraitEngine<'tcx>: 'tcx {
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>; fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>>; fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>; fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
@ -78,6 +78,6 @@ impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T {
return errors; return errors;
} }
self.collect_remaining_errors() self.collect_remaining_errors(infcx)
} }
} }

View file

@ -128,7 +128,11 @@ pub enum FulfillmentErrorCode<'tcx> {
CodeProjectionError(MismatchedProjectionTypes<'tcx>), CodeProjectionError(MismatchedProjectionTypes<'tcx>),
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
CodeConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>), CodeConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
CodeAmbiguity, CodeAmbiguity {
/// Overflow reported from the new solver `-Ztrait-solver=next`, which will
/// be reported as an regular error as opposed to a fatal error.
overflow: bool,
},
} }
impl<'tcx, O> Obligation<'tcx, O> { impl<'tcx, O> Obligation<'tcx, O> {

View file

@ -46,7 +46,8 @@ impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> {
super::CodeConstEquateError(ref a, ref b) => { super::CodeConstEquateError(ref a, ref b) => {
write!(f, "CodeConstEquateError({:?}, {:?})", a, b) write!(f, "CodeConstEquateError({:?}, {:?})", a, b)
} }
super::CodeAmbiguity => write!(f, "Ambiguity"), super::CodeAmbiguity { overflow: false } => write!(f, "Ambiguity"),
super::CodeAmbiguity { overflow: true } => write!(f, "Overflow"),
super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle), super::CodeCycle(ref cycle) => write!(f, "Cycle({:?})", cycle),
} }
} }

View file

@ -1,6 +1,7 @@
use std::mem; use std::mem;
use rustc_infer::infer::InferCtxt; use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::solve::MaybeCause;
use rustc_infer::traits::Obligation; use rustc_infer::traits::Obligation;
use rustc_infer::traits::{ use rustc_infer::traits::{
query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
@ -41,13 +42,31 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
self.obligations.push(obligation); self.obligations.push(obligation);
} }
fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { fn collect_remaining_errors(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
self.obligations self.obligations
.drain(..) .drain(..)
.map(|obligation| FulfillmentError { .map(|obligation| {
obligation: obligation.clone(), let code =
code: FulfillmentErrorCode::CodeAmbiguity, infcx.probe(|_| match infcx.evaluate_root_goal(obligation.clone().into()) {
root_obligation: obligation, Ok((_, Certainty::Maybe(MaybeCause::Ambiguity), _)) => {
FulfillmentErrorCode::CodeAmbiguity { overflow: false }
}
Ok((_, Certainty::Maybe(MaybeCause::Overflow), _)) => {
FulfillmentErrorCode::CodeAmbiguity { 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 {
obligation: obligation.clone(),
code,
root_obligation: obligation,
}
}) })
.collect() .collect()
} }

View file

@ -40,13 +40,16 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
self.obligations.insert(obligation); self.obligations.insert(obligation);
} }
fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { fn collect_remaining_errors(
&mut self,
_infcx: &InferCtxt<'tcx>,
) -> Vec<FulfillmentError<'tcx>> {
// any remaining obligations are errors // any remaining obligations are errors
self.obligations self.obligations
.iter() .iter()
.map(|obligation| FulfillmentError { .map(|obligation| FulfillmentError {
obligation: obligation.clone(), obligation: obligation.clone(),
code: FulfillmentErrorCode::CodeAmbiguity, code: FulfillmentErrorCode::CodeAmbiguity { overflow: false },
// FIXME - does Chalk have a notation of 'root obligation'? // FIXME - does Chalk have a notation of 'root obligation'?
// This is just for diagnostics, so it's okay if this is wrong // This is just for diagnostics, so it's okay if this is wrong
root_obligation: obligation.clone(), root_obligation: obligation.clone(),

View file

@ -125,6 +125,8 @@ pub trait TypeErrCtxtExt<'tcx> {
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>, + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug; <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed;
fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed; fn report_fulfillment_errors(&self, errors: &[FulfillmentError<'tcx>]) -> ErrorGuaranteed;
fn report_overflow_obligation<T>( fn report_overflow_obligation<T>(
@ -602,6 +604,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
); );
} }
fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed {
let obligation = self.resolve_vars_if_possible(obligation);
let mut err = self.build_overflow_error(&obligation.predicate, obligation.cause.span, true);
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
err.emit()
}
fn report_selection_error( fn report_selection_error(
&self, &self,
mut obligation: PredicateObligation<'tcx>, mut obligation: PredicateObligation<'tcx>,
@ -1658,9 +1668,12 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
FulfillmentErrorCode::CodeProjectionError(ref e) => { FulfillmentErrorCode::CodeProjectionError(ref e) => {
self.report_projection_error(&error.obligation, e); self.report_projection_error(&error.obligation, e);
} }
FulfillmentErrorCode::CodeAmbiguity => { FulfillmentErrorCode::CodeAmbiguity { overflow: false } => {
self.maybe_report_ambiguity(&error.obligation); self.maybe_report_ambiguity(&error.obligation);
} }
FulfillmentErrorCode::CodeAmbiguity { overflow: true } => {
self.report_overflow_no_abort(error.obligation.clone());
}
FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => { FulfillmentErrorCode::CodeSubtypeError(ref expected_found, ref err) => {
self.report_mismatched_types( self.report_mismatched_types(
&error.obligation.cause, &error.obligation.cause,

View file

@ -133,8 +133,15 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentContext<'tcx> {
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] }); .register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
} }
fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> { fn collect_remaining_errors(
self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect() &mut self,
_infcx: &InferCtxt<'tcx>,
) -> Vec<FulfillmentError<'tcx>> {
self.predicates
.to_errors(CodeAmbiguity { overflow: false })
.into_iter()
.map(to_fulfillment_error)
.collect()
} }
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> { fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {

View file

@ -1,9 +1,10 @@
error[E0282]: type annotations needed error[E0275]: overflow evaluating the requirement `for<'a> &'a mut Bar well-formed`
--> $DIR/issue-95230.rs:9:13 --> $DIR/issue-95230.rs:9:13
| |
LL | for<'a> &'a mut Self:; LL | for<'a> &'a mut Self:;
| ^^^^^^^^^^^^ cannot infer type for mutable reference `&'a mut Bar` | ^^^^^^^^^^^^
| |
= 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
| |
@ -15,4 +16,4 @@ LL | for<'a> &'a mut Self:;
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`. For more information about this error, try `rustc --explain E0275`.

View file

@ -13,7 +13,7 @@ fn needs_bar<S: Bar>() {}
fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() { fn test<T: Foo1<Assoc1 = <T as Foo2>::Assoc2> + Foo2<Assoc2 = <T as Foo1>::Assoc1>>() {
needs_bar::<T::Assoc1>(); needs_bar::<T::Assoc1>();
//~^ ERROR type annotations needed //~^ ERROR overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
} }
fn main() {} fn main() {}

View file

@ -1,10 +1,10 @@
error[E0283]: type annotations needed: cannot satisfy `<T as Foo1>::Assoc1: Bar` error[E0275]: overflow evaluating the requirement `<T as Foo1>::Assoc1: Bar`
--> $DIR/recursive-self-normalization-2.rs:15:5 --> $DIR/recursive-self-normalization-2.rs:15:5
| |
LL | needs_bar::<T::Assoc1>(); LL | needs_bar::<T::Assoc1>();
| ^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^
| |
= note: cannot satisfy `<T as Foo1>::Assoc1: Bar` = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`)
note: required by a bound in `needs_bar` note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization-2.rs:12:17 --> $DIR/recursive-self-normalization-2.rs:12:17
| |
@ -13,4 +13,4 @@ LL | fn needs_bar<S: Bar>() {}
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0283`. For more information about this error, try `rustc --explain E0275`.

View file

@ -9,7 +9,7 @@ fn needs_bar<S: Bar>() {}
fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() { fn test<T: Foo<Assoc = <T as Foo>::Assoc>>() {
needs_bar::<T::Assoc>(); needs_bar::<T::Assoc>();
//~^ ERROR type annotations needed //~^ ERROR overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
} }
fn main() {} fn main() {}

View file

@ -1,10 +1,10 @@
error[E0283]: type annotations needed: cannot satisfy `<T as Foo>::Assoc: Bar` error[E0275]: overflow evaluating the requirement `<T as Foo>::Assoc: Bar`
--> $DIR/recursive-self-normalization.rs:11:5 --> $DIR/recursive-self-normalization.rs:11:5
| |
LL | needs_bar::<T::Assoc>(); LL | needs_bar::<T::Assoc>();
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
| |
= note: cannot satisfy `<T as Foo>::Assoc: Bar` = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)
note: required by a bound in `needs_bar` note: required by a bound in `needs_bar`
--> $DIR/recursive-self-normalization.rs:8:17 --> $DIR/recursive-self-normalization.rs:8:17
| |
@ -13,4 +13,4 @@ LL | fn needs_bar<S: Bar>() {}
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0283`. For more information about this error, try `rustc --explain E0275`.