Skip to content

Commit

Permalink
Refactor how we handle overflow so that it is a fatal error that aborts
Browse files Browse the repository at this point in the history
compilation: this removes all the ungainly code that special cases
overflow so that we can ensure it propagates.
  • Loading branch information
nikomatsakis committed Mar 23, 2015
1 parent 809a554 commit 8bd8466
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 89 deletions.
54 changes: 35 additions & 19 deletions src/librustc/middle/traits/error_reporting.rs
Expand Up @@ -12,6 +12,7 @@ use super::{
FulfillmentError,
FulfillmentErrorCode,
MismatchedProjectionTypes,
Obligation,
ObligationCauseCode,
OutputTypeParameterMismatch,
PredicateObligation,
Expand All @@ -21,6 +22,7 @@ use super::{
use fmt_macros::{Parser, Piece, Position};
use middle::infer::InferCtxt;
use middle::ty::{self, AsPredicate, ReferencesError, ToPolyTraitRef, TraitRef};
use middle::ty_fold::TypeFoldable;
use std::collections::HashMap;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::attr::{AttributeMethods, AttrMetaMethods};
Expand Down Expand Up @@ -137,24 +139,36 @@ fn report_on_unimplemented<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
report
}

/// Reports that an overflow has occurred and halts compilation. We
/// halt compilation unconditionally because it is important that
/// overflows never be masked -- they basically represent computations
/// whose result could not be truly determined and thus we can't say
/// if the program type checks or not -- and they are unusual
/// occurrences in any case.
pub fn report_overflow_error<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &Obligation<'tcx, T>)
-> !
where T: UserString<'tcx> + TypeFoldable<'tcx>
{
let predicate =
infcx.resolve_type_vars_if_possible(&obligation.predicate);
span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
"overflow evaluating the requirement `{}`",
predicate.user_string(infcx.tcx));

suggest_new_overflow_limit(infcx.tcx, obligation.cause.span);

note_obligation_cause(infcx, obligation);

infcx.tcx.sess.abort_if_errors();
unreachable!();
}

pub fn report_selection_error<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &PredicateObligation<'tcx>,
error: &SelectionError<'tcx>)
{
match *error {
SelectionError::Overflow => {
// We could track the stack here more precisely if we wanted, I imagine.
let predicate =
infcx.resolve_type_vars_if_possible(&obligation.predicate);
span_err!(infcx.tcx.sess, obligation.cause.span, E0275,
"overflow evaluating the requirement `{}`",
predicate.user_string(infcx.tcx));

suggest_new_overflow_limit(infcx.tcx, obligation.cause.span);

note_obligation_cause(infcx, obligation);
}

SelectionError::Unimplemented => {
match &obligation.cause.code {
&ObligationCauseCode::CompareImplMethodObligation => {
Expand Down Expand Up @@ -309,19 +323,21 @@ pub fn maybe_report_ambiguity<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
}
}

fn note_obligation_cause<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &PredicateObligation<'tcx>)
fn note_obligation_cause<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
obligation: &Obligation<'tcx, T>)
where T: UserString<'tcx>
{
note_obligation_cause_code(infcx,
&obligation.predicate,
obligation.cause.span,
&obligation.cause.code);
}

fn note_obligation_cause_code<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>,
predicate: &ty::Predicate<'tcx>,
cause_span: Span,
cause_code: &ObligationCauseCode<'tcx>)
fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
predicate: &T,
cause_span: Span,
cause_code: &ObligationCauseCode<'tcx>)
where T: UserString<'tcx>
{
let tcx = infcx.tcx;
match *cause_code {
Expand Down
36 changes: 6 additions & 30 deletions src/librustc/middle/traits/mod.rs
Expand Up @@ -23,9 +23,10 @@ use std::slice::Iter;
use std::rc::Rc;
use syntax::ast;
use syntax::codemap::{Span, DUMMY_SP};
use util::ppaux::{Repr, UserString};
use util::ppaux::Repr;

pub use self::error_reporting::report_fulfillment_errors;
pub use self::error_reporting::report_overflow_error;
pub use self::error_reporting::suggest_new_overflow_limit;
pub use self::coherence::orphan_check;
pub use self::coherence::overlapping_impls;
Expand Down Expand Up @@ -151,7 +152,6 @@ pub type Selection<'tcx> = Vtable<'tcx, PredicateObligation<'tcx>>;
#[derive(Clone,Debug)]
pub enum SelectionError<'tcx> {
Unimplemented,
Overflow,
OutputTypeParameterMismatch(ty::PolyTraitRef<'tcx>,
ty::PolyTraitRef<'tcx>,
ty::type_err<'tcx>),
Expand Down Expand Up @@ -327,16 +327,9 @@ pub fn evaluate_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
let result = match fulfill_cx.select_all_or_error(infcx, typer) {
Ok(()) => Ok(Some(())), // Success, we know it implements Copy.
Err(errors) => {
// Check if overflow occurred anywhere and propagate that.
if errors.iter().any(
|err| match err.code { CodeSelectionError(Overflow) => true, _ => false })
{
return Err(Overflow);
}

// Otherwise, if there were any hard errors, propagate an
// arbitrary one of those. If no hard errors at all,
// report ambiguity.
// If there were any hard errors, propagate an arbitrary
// one of those. If no hard errors at all, report
// ambiguity.
let sel_error =
errors.iter()
.filter_map(|err| {
Expand Down Expand Up @@ -384,16 +377,8 @@ pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>,
// soldering on, so just treat this like not implemented
false
}
Err(Overflow) => {
span_err!(infcx.tcx.sess, span, E0285,
"overflow evaluating whether `{}` is `{}`",
ty.user_string(infcx.tcx),
bound.user_string(infcx.tcx));
suggest_new_overflow_limit(infcx.tcx, span);
false
}
Err(_) => {
// other errors: not implemented.
// errors: not implemented.
false
}
}
Expand Down Expand Up @@ -652,15 +637,6 @@ impl<'tcx> FulfillmentError<'tcx> {
{
FulfillmentError { obligation: obligation, code: code }
}

pub fn is_overflow(&self) -> bool {
match self.code {
CodeAmbiguity => false,
CodeSelectionError(Overflow) => true,
CodeSelectionError(_) => false,
CodeProjectionError(_) => false,
}
}
}

impl<'tcx> TraitObligation<'tcx> {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/traits/project.rs
Expand Up @@ -11,9 +11,9 @@
//! Code for projecting associated types out of trait references.

use super::elaborate_predicates;
use super::report_overflow_error;
use super::Obligation;
use super::ObligationCause;
use super::Overflow;
use super::PredicateObligation;
use super::SelectionContext;
use super::SelectionError;
Expand Down Expand Up @@ -442,7 +442,7 @@ fn project_type<'cx,'tcx>(
let recursion_limit = selcx.tcx().sess.recursion_limit.get();
if obligation.recursion_depth >= recursion_limit {
debug!("project: overflow!");
return Err(ProjectionTyError::TraitSelectionError(Overflow));
report_overflow_error(selcx.infcx(), &obligation);
}

let obligation_trait_ref =
Expand Down
20 changes: 10 additions & 10 deletions src/librustc/middle/traits/select.rs
Expand Up @@ -21,8 +21,9 @@ use super::DerivedObligationCause;
use super::project;
use super::project::{normalize_with_depth, Normalized};
use super::{PredicateObligation, TraitObligation, ObligationCause};
use super::{report_overflow_error};
use super::{ObligationCauseCode, BuiltinDerivedObligation, ImplDerivedObligation};
use super::{SelectionError, Unimplemented, Overflow, OutputTypeParameterMismatch};
use super::{SelectionError, Unimplemented, OutputTypeParameterMismatch};
use super::{Selection};
use super::{SelectionResult};
use super::{VtableBuiltin, VtableImpl, VtableParam, VtableClosure,
Expand Down Expand Up @@ -561,10 +562,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
// not update) the cache.
let recursion_limit = self.infcx.tcx.sess.recursion_limit.get();
if stack.obligation.recursion_depth >= recursion_limit {
debug!("{} --> overflow (limit={})",
stack.obligation.repr(self.tcx()),
recursion_limit);
return Err(Overflow)
report_overflow_error(self.infcx(), &stack.obligation);
}

// Check the cache. Note that we skolemize the trait-ref
Expand Down Expand Up @@ -2582,11 +2580,13 @@ impl<'o, 'tcx> Repr<'tcx> for TraitObligationStack<'o, 'tcx> {
impl<'tcx> EvaluationResult<'tcx> {
fn may_apply(&self) -> bool {
match *self {
EvaluatedToOk
| EvaluatedToAmbig
| EvaluatedToErr(Overflow)
| EvaluatedToErr(OutputTypeParameterMismatch(..)) => true,
EvaluatedToErr(Unimplemented) => false,
EvaluatedToOk |
EvaluatedToAmbig |
EvaluatedToErr(OutputTypeParameterMismatch(..)) =>
true,

EvaluatedToErr(Unimplemented) =>
false,
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions src/librustc/middle/traits/util.rs
Expand Up @@ -514,9 +514,6 @@ impl<'tcx> Repr<'tcx> for super::VtableObjectData<'tcx> {
impl<'tcx> Repr<'tcx> for super::SelectionError<'tcx> {
fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
match *self {
super::Overflow =>
format!("Overflow"),

super::Unimplemented =>
format!("Unimplemented"),

Expand Down
21 changes: 7 additions & 14 deletions src/librustc_trans/trans/common.rs
Expand Up @@ -1025,8 +1025,9 @@ pub fn fulfill_obligation<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// shallow result we are looking for -- that is, what specific impl.
let typer = NormalizingClosureTyper::new(tcx);
let mut selcx = traits::SelectionContext::new(&infcx, &typer);
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
trait_ref.to_poly_trait_predicate());
let obligation =
traits::Obligation::new(traits::ObligationCause::misc(span, ast::DUMMY_NODE_ID),
trait_ref.to_poly_trait_predicate());
let selection = match selcx.select(&obligation) {
Ok(Some(selection)) => selection,
Ok(None) => {
Expand Down Expand Up @@ -1081,7 +1082,7 @@ pub fn predicates_hold<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), predicate);
fulfill_cx.register_predicate_obligation(&infcx, obligation);
}
drain_fulfillment_cx(DUMMY_SP, &infcx, &mut fulfill_cx, &()).is_ok()
drain_fulfillment_cx(&infcx, &mut fulfill_cx, &()).is_ok()
}

pub struct NormalizingClosureTyper<'a,'tcx:'a> {
Expand Down Expand Up @@ -1138,7 +1139,7 @@ pub fn drain_fulfillment_cx_or_panic<'a,'tcx,T>(span: Span,
-> T
where T : TypeFoldable<'tcx> + Repr<'tcx>
{
match drain_fulfillment_cx(span, infcx, fulfill_cx, result) {
match drain_fulfillment_cx(infcx, fulfill_cx, result) {
Ok(v) => v,
Err(errors) => {
infcx.tcx.sess.span_bug(
Expand All @@ -1156,8 +1157,7 @@ pub fn drain_fulfillment_cx_or_panic<'a,'tcx,T>(span: Span,
/// inference variables that appear in `result` to be unified, and
/// hence we need to process those obligations to get the complete
/// picture of the type.
pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
infcx: &infer::InferCtxt<'a,'tcx>,
pub fn drain_fulfillment_cx<'a,'tcx,T>(infcx: &infer::InferCtxt<'a,'tcx>,
fulfill_cx: &mut traits::FulfillmentContext<'tcx>,
result: &T)
-> StdResult<T,Vec<traits::FulfillmentError<'tcx>>>
Expand All @@ -1173,14 +1173,7 @@ pub fn drain_fulfillment_cx<'a,'tcx,T>(span: Span,
match fulfill_cx.select_all_or_error(infcx, &typer) {
Ok(()) => { }
Err(errors) => {
// We always want to surface any overflow errors, no matter what.
if errors.iter().all(|e| e.is_overflow()) {
infcx.tcx.sess.span_fatal(
span,
"reached the recursion limit during monomorphization");
} else {
return Err(errors);
}
return Err(errors);
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/test/compile-fail/issue-18400.rs
Expand Up @@ -33,7 +33,4 @@ fn main() {

0.contains(bits);
//~^ ERROR overflow
//~| ERROR overflow
//~| ERROR overflow
//~| ERROR mismatched types
}
9 changes: 1 addition & 8 deletions src/test/compile-fail/recursion_limit.rs
Expand Up @@ -42,12 +42,5 @@ fn is_send<T:Send>() { }
fn main() {
is_send::<A>();
//~^ ERROR overflow evaluating
//~^^ NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate
//~^^^ NOTE required by `is_send`
//~^^^^ ERROR overflow evaluating
//~^^^^^ NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate
//~^^^^^^ NOTE required by `is_send`
//~^^^^^^^ ERROR overflow evaluating
//~^^^^^^^^ NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate
//~^^^^^^^^^ NOTE required by `is_send`
//~| NOTE consider adding a `#![recursion_limit="20"]` attribute to your crate
}

0 comments on commit 8bd8466

Please sign in to comment.