From 8bd8466e812e7c5acb99a50493e45aeb1bb81e93 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 20 Mar 2015 06:48:40 -0400 Subject: [PATCH] Refactor how we handle overflow so that it is a fatal error that aborts compilation: this removes all the ungainly code that special cases overflow so that we can ensure it propagates. --- src/librustc/middle/traits/error_reporting.rs | 54 ++++++++++++------- src/librustc/middle/traits/mod.rs | 36 +++---------- src/librustc/middle/traits/project.rs | 4 +- src/librustc/middle/traits/select.rs | 20 +++---- src/librustc/middle/traits/util.rs | 3 -- src/librustc_trans/trans/common.rs | 21 +++----- src/test/compile-fail/issue-18400.rs | 3 -- src/test/compile-fail/recursion_limit.rs | 9 +--- 8 files changed, 61 insertions(+), 89 deletions(-) diff --git a/src/librustc/middle/traits/error_reporting.rs b/src/librustc/middle/traits/error_reporting.rs index d2b5b460d1420..d10ff060418cc 100644 --- a/src/librustc/middle/traits/error_reporting.rs +++ b/src/librustc/middle/traits/error_reporting.rs @@ -12,6 +12,7 @@ use super::{ FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + Obligation, ObligationCauseCode, OutputTypeParameterMismatch, PredicateObligation, @@ -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}; @@ -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 => { @@ -309,8 +323,9 @@ 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, @@ -318,10 +333,11 @@ fn note_obligation_cause<'a, 'tcx>(infcx: &InferCtxt<'a, 'tcx>, &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 { diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index f46cac308287b..24b201c960f16 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -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; @@ -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>), @@ -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| { @@ -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 } } @@ -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> { diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs index 6b66d7227d300..a67410998c727 100644 --- a/src/librustc/middle/traits/project.rs +++ b/src/librustc/middle/traits/project.rs @@ -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; @@ -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 = diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs index 7dfbccea0dccd..026ef7d4d0e44 100644 --- a/src/librustc/middle/traits/select.rs +++ b/src/librustc/middle/traits/select.rs @@ -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, @@ -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 @@ -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, } } } diff --git a/src/librustc/middle/traits/util.rs b/src/librustc/middle/traits/util.rs index 88b721ce95862..965aaf12044ec 100644 --- a/src/librustc/middle/traits/util.rs +++ b/src/librustc/middle/traits/util.rs @@ -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"), diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 8f5dbfe2ec000..69846d4f0e4e4 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -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) => { @@ -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> { @@ -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( @@ -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>> @@ -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); } } diff --git a/src/test/compile-fail/issue-18400.rs b/src/test/compile-fail/issue-18400.rs index 015f1fa603a20..f8d85f939374d 100644 --- a/src/test/compile-fail/issue-18400.rs +++ b/src/test/compile-fail/issue-18400.rs @@ -33,7 +33,4 @@ fn main() { 0.contains(bits); //~^ ERROR overflow - //~| ERROR overflow - //~| ERROR overflow - //~| ERROR mismatched types } diff --git a/src/test/compile-fail/recursion_limit.rs b/src/test/compile-fail/recursion_limit.rs index e8bc11317f2aa..368269999a296 100644 --- a/src/test/compile-fail/recursion_limit.rs +++ b/src/test/compile-fail/recursion_limit.rs @@ -42,12 +42,5 @@ fn is_send() { } fn main() { 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` - //~^^^^^^^ 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 }