about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorNick Cameron <ncameron@mozilla.com>2016-01-21 13:19:20 +1300
committerNick Cameron <ncameron@mozilla.com>2016-01-26 17:51:11 +1300
commit0b511e82abe48f40808453de2f013e7c5ebab4c3 (patch)
tree37bf874ace5ff71ac0358c4b976d35349268037b /src
parentfaf6d1e87391b25196b35909c3c95e5d873cacf0 (diff)
downloadrust-0b511e82abe48f40808453de2f013e7c5ebab4c3.tar.gz
rust-0b511e82abe48f40808453de2f013e7c5ebab4c3.zip
Initial work towards abort-free compilation
The goal is that the compiler will pass `Result`s around rather than using abort_if_errors. To preserve behaviour we currently abort at the top level. I've removed all other aborts from the driver, but haven't touched any of the nested aborts.
Diffstat (limited to 'src')
-rw-r--r--src/librustc/session/mod.rs23
-rw-r--r--src/librustc_driver/driver.rs405
-rw-r--r--src/librustc_driver/lib.rs73
-rw-r--r--src/librustc_driver/pretty.rs62
-rw-r--r--src/librustdoc/core.rs18
-rw-r--r--src/librustdoc/test.rs7
-rw-r--r--src/test/run-make/execution-engine/test.rs6
-rw-r--r--src/test/run-make/issue-19371/foo.rs2
8 files changed, 331 insertions, 265 deletions
diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs
index 975ec0e709b..fbfdbd15116 100644
--- a/src/librustc/session/mod.rs
+++ b/src/librustc/session/mod.rs
@@ -176,15 +176,28 @@ impl Session {
     pub fn abort_if_errors(&self) {
         self.diagnostic().abort_if_errors();
     }
-    pub fn abort_if_new_errors<F, T>(&self, f: F) -> T
+    pub fn track_errors<F, T>(&self, f: F) -> Result<T, usize>
         where F: FnOnce() -> T
     {
-        let count = self.err_count();
+        let mut count = self.err_count();
         let result = f();
-        if self.err_count() > count {
-            self.abort_if_errors();
+        count -= self.err_count();
+        if count == 0 {
+            Ok(result)
+        } else {
+            Err(count)
+        }
+    }
+    pub fn abort_if_new_errors<F, T>(&self, f: F) -> T
+        where F: FnOnce() -> T
+    {
+        match self.track_errors(f) {
+            Ok(result) => result,
+            Err(_) => {
+                self.abort_if_errors();
+                unreachable!();
+            }
         }
-        result
     }
     pub fn span_warn(&self, sp: Span, msg: &str) {
         self.diagnostic().span_warn(sp, msg)
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index 36074a7ae02..87ac9a2052b 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -35,7 +35,7 @@ use rustc_plugin as plugin;
 use rustc_front::hir;
 use rustc_front::lowering::{lower_crate, LoweringContext};
 use rustc_passes::{no_asm, loops, consts, const_fn, rvalues, static_recursion};
-use super::Compilation;
+use super::{Compilation, CompileResult, compile_result_from_err_count};
 
 use serialize::json;
 
@@ -57,45 +57,50 @@ use syntax::visit;
 use syntax;
 use syntax_ext;
 
-pub fn compile_input(sess: Session,
+macro_rules! throw_if_errors {
+    ($tsess: expr) => {{
+        let err_count = $tsess.err_count();
+        if err_count > 0 {
+            return Err(err_count);
+        }
+    }}
+}
+
+pub fn compile_input(sess: &Session,
                      cstore: &CStore,
                      cfg: ast::CrateConfig,
                      input: &Input,
                      outdir: &Option<PathBuf>,
                      output: &Option<PathBuf>,
                      addl_plugins: Option<Vec<String>>,
-                     control: CompileController) {
+                     control: CompileController) -> CompileResult {
     macro_rules! controller_entry_point{($point: ident, $tsess: expr, $make_state: expr) => ({
         let state = $make_state;
         (control.$point.callback)(state);
 
         if control.$point.stop == Compilation::Stop {
-            $tsess.abort_if_errors();
-            return;
+            return compile_result_from_err_count($tsess.err_count());
         }
     })}
 
     // We need nested scopes here, because the intermediate results can keep
     // large chunks of memory alive and we want to free them as soon as
     // possible to keep the peak memory usage low
-    let result = {
+    let (outputs, trans) = {
         let (outputs, expanded_crate, id) = {
-            let krate = phase_1_parse_input(&sess, cfg, input);
+            let krate = phase_1_parse_input(sess, cfg, input);
 
             controller_entry_point!(after_parse,
                                     sess,
-                                    CompileState::state_after_parse(input, &sess, outdir, &krate));
-
-            let outputs = build_output_filenames(input, outdir, output, &krate.attrs, &sess);
-            let id = link::find_crate_name(Some(&sess), &krate.attrs, input);
-            let expanded_crate = match phase_2_configure_and_expand(&sess,
-                                                                    &cstore,
-                                                                    krate,
-                                                                    &id[..],
-                                                                    addl_plugins) {
-                None => return,
-                Some(k) => k,
-            };
+                                    CompileState::state_after_parse(input, sess, outdir, &krate));
+
+            let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess);
+            let id = link::find_crate_name(Some(sess), &krate.attrs, input);
+            let expanded_crate = try!(phase_2_configure_and_expand(sess,
+                                                                   &cstore,
+                                                                   krate,
+                                                                   &id[..],
+                                                                   addl_plugins));
 
             (outputs, expanded_crate, id)
         };
@@ -103,14 +108,14 @@ pub fn compile_input(sess: Session,
         controller_entry_point!(after_expand,
                                 sess,
                                 CompileState::state_after_expand(input,
-                                                                 &sess,
+                                                                 sess,
                                                                  outdir,
                                                                  &expanded_crate,
                                                                  &id[..]));
 
-        let expanded_crate = assign_node_ids(&sess, expanded_crate);
+        let expanded_crate = assign_node_ids(sess, expanded_crate);
         // Lower ast -> hir.
-        let lcx = LoweringContext::new(&sess, Some(&expanded_crate));
+        let lcx = LoweringContext::new(sess, Some(&expanded_crate));
         let mut hir_forest = time(sess.time_passes(),
                                   "lowering ast -> hir",
                                   || hir_map::Forest::new(lower_crate(&lcx, &expanded_crate)));
@@ -122,14 +127,14 @@ pub fn compile_input(sess: Session,
         }
 
         let arenas = ty::CtxtArenas::new();
-        let hir_map = make_map(&sess, &mut hir_forest);
+        let hir_map = make_map(sess, &mut hir_forest);
 
-        write_out_deps(&sess, &outputs, &id);
+        write_out_deps(sess, &outputs, &id);
 
         controller_entry_point!(after_write_deps,
                                 sess,
                                 CompileState::state_after_write_deps(input,
-                                                                     &sess,
+                                                                     sess,
                                                                      outdir,
                                                                      &hir_map,
                                                                      &expanded_crate,
@@ -138,12 +143,12 @@ pub fn compile_input(sess: Session,
                                                                      &lcx));
 
         time(sess.time_passes(), "attribute checking", || {
-            front::check_attr::check_crate(&sess, &expanded_crate);
+            front::check_attr::check_crate(sess, &expanded_crate);
         });
 
         time(sess.time_passes(),
              "early lint checks",
-             || lint::check_ast_crate(&sess, &expanded_crate));
+             || lint::check_ast_crate(sess, &expanded_crate));
 
         let opt_crate = if sess.opts.debugging_opts.keep_ast ||
                            sess.opts.debugging_opts.save_analysis {
@@ -153,67 +158,62 @@ pub fn compile_input(sess: Session,
             None
         };
 
-        phase_3_run_analysis_passes(&sess,
-                                    &cstore,
-                                    hir_map,
-                                    &arenas,
-                                    &id,
-                                    control.make_glob_map,
-                                    |tcx, mir_map, analysis| {
-
-                                        {
-                                            let state =
-                                                CompileState::state_after_analysis(input,
-                                                                                   &tcx.sess,
-                                                                                   outdir,
-                                                                                   opt_crate,
-                                                                                   tcx.map.krate(),
-                                                                                   &analysis,
-                                                                                   &mir_map,
-                                                                                   tcx,
-                                                                                   &lcx,
-                                                                                   &id);
-                                            (control.after_analysis.callback)(state);
-
-                                            tcx.sess.abort_if_errors();
-                                            if control.after_analysis.stop == Compilation::Stop {
-                                                return Err(());
-                                            }
-                                        }
-
-                                        if log_enabled!(::log::INFO) {
-                                            println!("Pre-trans");
-                                            tcx.print_debug_stats();
-                                        }
-                                        let trans = phase_4_translate_to_llvm(tcx,
-                                                                              mir_map,
-                                                                              analysis);
-
-                                        if log_enabled!(::log::INFO) {
-                                            println!("Post-trans");
-                                            tcx.print_debug_stats();
-                                        }
-
-                                        // Discard interned strings as they are no longer required.
-                                        token::get_ident_interner().clear();
-
-                                        Ok((outputs, trans))
-                                    })
-    };
+        try!(try!(phase_3_run_analysis_passes(sess,
+                                         &cstore,
+                                         hir_map,
+                                         &arenas,
+                                         &id,
+                                         control.make_glob_map,
+                                         |tcx, mir_map, analysis| {
+            {
+                let state =
+                    CompileState::state_after_analysis(input,
+                                                       &tcx.sess,
+                                                       outdir,
+                                                       opt_crate,
+                                                       tcx.map.krate(),
+                                                       &analysis,
+                                                       &mir_map,
+                                                       tcx,
+                                                       &lcx,
+                                                       &id);
+                (control.after_analysis.callback)(state);
+
+                throw_if_errors!(tcx.sess);
+                if control.after_analysis.stop == Compilation::Stop {
+                    return Err(0usize);
+                }
+            }
 
-    let (outputs, trans) = if let Ok(out) = result {
-        out
-    } else {
-        return;
+            if log_enabled!(::log::INFO) {
+                println!("Pre-trans");
+                tcx.print_debug_stats();
+            }
+            let trans = phase_4_translate_to_llvm(tcx,
+                                                  mir_map,
+                                                  analysis);
+
+            if log_enabled!(::log::INFO) {
+                println!("Post-trans");
+                tcx.print_debug_stats();
+            }
+
+            // Discard interned strings as they are no longer required.
+            token::get_ident_interner().clear();
+
+            Ok((outputs, trans))
+        })))
     };
 
-    phase_5_run_llvm_passes(&sess, &trans, &outputs);
+    try!(phase_5_run_llvm_passes(sess, &trans, &outputs));
 
     controller_entry_point!(after_llvm,
                             sess,
-                            CompileState::state_after_llvm(input, &sess, outdir, &trans));
+                            CompileState::state_after_llvm(input, sess, outdir, &trans));
+
+    phase_6_link_output(sess, &trans, &outputs);
 
-    phase_6_link_output(&sess, &trans, &outputs);
+    Ok(())
 }
 
 /// The name used for source code that doesn't originate in a file
@@ -457,7 +457,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
                                     mut krate: ast::Crate,
                                     crate_name: &str,
                                     addl_plugins: Option<Vec<String>>)
-                                    -> Option<ast::Crate> {
+                                    -> Result<ast::Crate, usize> {
     let time_passes = sess.time_passes();
 
     // strip before anything else because crate metadata may use #[cfg_attr]
@@ -469,13 +469,13 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // baz! should not use this definition unless foo is enabled.
 
     let mut feature_gated_cfgs = vec![];
-    krate = time(time_passes, "configuration 1", || {
-        sess.abort_if_new_errors(|| {
+    krate = try!(time(time_passes, "configuration 1", || {
+        sess.track_errors(|| {
             syntax::config::strip_unconfigured_items(sess.diagnostic(),
                                                      krate,
                                                      &mut feature_gated_cfgs)
         })
-    });
+    }));
 
     *sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
     *sess.crate_metadata.borrow_mut() = collect_crate_metadata(sess, &krate.attrs);
@@ -484,8 +484,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         middle::recursion_limit::update_recursion_limit(sess, &krate);
     });
 
-    time(time_passes, "gated macro checking", || {
-        sess.abort_if_new_errors(|| {
+    try!(time(time_passes, "gated macro checking", || {
+        sess.track_errors(|| {
             let features =
               syntax::feature_gate::check_crate_macros(sess.codemap(),
                                                        &sess.parse_sess.span_diagnostic,
@@ -493,8 +493,8 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
             // these need to be set "early" so that expansion sees `quote` if enabled.
             *sess.features.borrow_mut() = features;
-        });
-    });
+        })
+    }));
 
 
     krate = time(time_passes, "crate injection", || {
@@ -531,7 +531,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     let Registry { syntax_exts, early_lint_passes, late_lint_passes, lint_groups,
                    llvm_passes, attributes, .. } = registry;
 
-    sess.abort_if_new_errors(|| {
+    try!(sess.track_errors(|| {
         let mut ls = sess.lint_store.borrow_mut();
         for pass in early_lint_passes {
             ls.register_early_pass(Some(sess), true, pass);
@@ -546,14 +546,14 @@ pub fn phase_2_configure_and_expand(sess: &Session,
 
         *sess.plugin_llvm_passes.borrow_mut() = llvm_passes;
         *sess.plugin_attributes.borrow_mut() = attributes.clone();
-    });
+    }));
 
     // Lint plugins are registered; now we can process command line flags.
     if sess.opts.describe_lints {
         super::describe_lints(&*sess.lint_store.borrow(), true);
-        return None;
+        return Err(0);
     }
-    sess.abort_if_new_errors(|| sess.lint_store.borrow_mut().process_command_line(sess));
+    try!(sess.track_errors(|| sess.lint_store.borrow_mut().process_command_line(sess)));
 
     krate = time(time_passes, "expansion", || {
         // Windows dlls do not have rpaths, so they don't know how to find their
@@ -596,21 +596,21 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // of macro expansion.  This runs before #[cfg] to try to catch as
     // much as possible (e.g. help the programmer avoid platform
     // specific differences)
-    time(time_passes, "complete gated feature checking 1", || {
-        sess.abort_if_new_errors(|| {
+    try!(time(time_passes, "complete gated feature checking 1", || {
+        sess.track_errors(|| {
             let features = syntax::feature_gate::check_crate(sess.codemap(),
                                                              &sess.parse_sess.span_diagnostic,
                                                              &krate,
                                                              &attributes,
                                                              sess.opts.unstable_features);
             *sess.features.borrow_mut() = features;
-        });
-    });
+        })
+    }));
 
     // JBC: make CFG processing part of expansion to avoid this problem:
 
     // strip again, in case expansion added anything with a #[cfg].
-    krate = sess.abort_if_new_errors(|| {
+    krate = try!(sess.track_errors(|| {
         let krate = time(time_passes, "configuration 2", || {
             syntax::config::strip_unconfigured_items(sess.diagnostic(),
                                                      krate,
@@ -627,7 +627,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         });
 
         krate
-    });
+    }));
 
     krate = time(time_passes, "maybe building test harness", || {
         syntax::test::modify_for_testing(&sess.parse_sess, &sess.opts.cfg, krate, sess.diagnostic())
@@ -648,16 +648,16 @@ pub fn phase_2_configure_and_expand(sess: &Session,
     // One final feature gating of the true AST that gets compiled
     // later, to make sure we've got everything (e.g. configuration
     // can insert new attributes via `cfg_attr`)
-    time(time_passes, "complete gated feature checking 2", || {
-        sess.abort_if_new_errors(|| {
+    try!(time(time_passes, "complete gated feature checking 2", || {
+        sess.track_errors(|| {
             let features = syntax::feature_gate::check_crate(sess.codemap(),
                                                              &sess.parse_sess.span_diagnostic,
                                                              &krate,
                                                              &attributes,
                                                              sess.opts.unstable_features);
             *sess.features.borrow_mut() = features;
-        });
-    });
+        })
+    }));
 
     time(time_passes,
          "const fn bodies and arguments",
@@ -667,7 +667,7 @@ pub fn phase_2_configure_and_expand(sess: &Session,
         println!("Post-expansion node count: {}", count_nodes(&krate));
     }
 
-    Some(krate)
+    Ok(krate)
 }
 
 pub fn assign_node_ids(sess: &Session, krate: ast::Crate) -> ast::Crate {
@@ -712,7 +712,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                                name: &str,
                                                make_glob_map: resolve::MakeGlobMap,
                                                f: F)
-                                               -> R
+                                               -> Result<R, usize>
     where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, MirMap<'tcx>, ty::CrateAnalysis) -> R
 {
     let time_passes = sess.time_passes();
@@ -722,11 +722,11 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
          "external crate/lib resolution",
          || LocalCrateReader::new(sess, cstore, &hir_map).read_crates(krate));
 
-    let lang_items = time(time_passes, "language item collection", || {
-        sess.abort_if_new_errors(|| {
+    let lang_items = try!(time(time_passes, "language item collection", || {
+        sess.track_errors(|| {
             middle::lang_items::collect_language_items(&sess, &hir_map)
         })
-    });
+    }));
 
     let resolve::CrateMap {
         def_map,
@@ -773,98 +773,98 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
                                lang_items,
                                stability::Index::new(krate),
                                |tcx| {
-                                   // passes are timed inside typeck
-                                   typeck::check_crate(tcx, trait_map);
-
-                                   time(time_passes,
-                                        "const checking",
-                                        || consts::check_crate(tcx));
-
-                                   let access_levels =
-                                       time(time_passes, "privacy checking", || {
-                                           rustc_privacy::check_crate(tcx,
-                                                                      &export_map,
-                                                                      external_exports)
-                                       });
-
-                                   // Do not move this check past lint
-                                   time(time_passes, "stability index", || {
-                                       tcx.stability.borrow_mut().build(tcx, krate, &access_levels)
-                                   });
-
-                                   time(time_passes,
-                                        "intrinsic checking",
-                                        || middle::intrinsicck::check_crate(tcx));
-
-                                   time(time_passes,
-                                        "effect checking",
-                                        || middle::effect::check_crate(tcx));
-
-                                   time(time_passes,
-                                        "match checking",
-                                        || middle::check_match::check_crate(tcx));
-
-                                   let mir_map =
-                                       time(time_passes,
-                                            "MIR dump",
-                                            || mir::mir_map::build_mir_for_crate(tcx));
-
-                                   time(time_passes,
-                                        "liveness checking",
-                                        || middle::liveness::check_crate(tcx));
-
-                                   time(time_passes,
-                                        "borrow checking",
-                                        || borrowck::check_crate(tcx));
-
-                                   time(time_passes,
-                                        "rvalue checking",
-                                        || rvalues::check_crate(tcx));
-
-                                   // Avoid overwhelming user with errors if type checking failed.
-                                   // I'm not sure how helpful this is, to be honest, but it avoids
-                                   // a
-                                   // lot of annoying errors in the compile-fail tests (basically,
-                                   // lint warnings and so on -- kindck used to do this abort, but
-                                   // kindck is gone now). -nmatsakis
-                                   tcx.sess.abort_if_errors();
-
-                                   let reachable_map =
-                                       time(time_passes,
-                                            "reachability checking",
-                                            || reachable::find_reachable(tcx, &access_levels));
-
-                                   time(time_passes, "death checking", || {
-                                       middle::dead::check_crate(tcx, &access_levels);
-                                   });
-
-                                   let ref lib_features_used =
-                                       time(time_passes,
-                                            "stability checking",
-                                            || stability::check_unstable_api_usage(tcx));
-
-                                   time(time_passes, "unused lib feature checking", || {
-                                       stability::check_unused_or_stable_features(&tcx.sess,
-                                                                                  lib_features_used)
-                                   });
-
-                                   time(time_passes,
-                                        "lint checking",
-                                        || lint::check_crate(tcx, &access_levels));
-
-                                   // The above three passes generate errors w/o aborting
-                                   tcx.sess.abort_if_errors();
-
-                                   f(tcx,
-                                     mir_map,
-                                     ty::CrateAnalysis {
-                                         export_map: export_map,
-                                         access_levels: access_levels,
-                                         reachable: reachable_map,
-                                         name: name,
-                                         glob_map: glob_map,
-                                     })
-                               })
+        // passes are timed inside typeck
+        typeck::check_crate(tcx, trait_map);
+
+        time(time_passes,
+             "const checking",
+             || middle::check_const::check_crate(tcx));
+
+        let access_levels =
+            time(time_passes, "privacy checking", || {
+                rustc_privacy::check_crate(tcx,
+                                           &export_map,
+                                           external_exports)
+            });
+
+        // Do not move this check past lint
+        time(time_passes, "stability index", || {
+            tcx.stability.borrow_mut().build(tcx, krate, &access_levels)
+        });
+
+        time(time_passes,
+             "intrinsic checking",
+             || middle::intrinsicck::check_crate(tcx));
+
+        time(time_passes,
+             "effect checking",
+             || middle::effect::check_crate(tcx));
+
+        time(time_passes,
+             "match checking",
+             || middle::check_match::check_crate(tcx));
+
+        let mir_map =
+            time(time_passes,
+                 "MIR dump",
+                 || mir::mir_map::build_mir_for_crate(tcx));
+
+        time(time_passes,
+             "liveness checking",
+             || middle::liveness::check_crate(tcx));
+
+        time(time_passes,
+             "borrow checking",
+             || borrowck::check_crate(tcx));
+
+        time(time_passes,
+             "rvalue checking",
+             || middle::check_rvalues::check_crate(tcx));
+
+        // Avoid overwhelming user with errors if type checking failed.
+        // I'm not sure how helpful this is, to be honest, but it avoids
+        // a
+        // lot of annoying errors in the compile-fail tests (basically,
+        // lint warnings and so on -- kindck used to do this abort, but
+        // kindck is gone now). -nmatsakis
+        throw_if_errors!(tcx.sess);
+
+        let reachable_map =
+            time(time_passes,
+                 "reachability checking",
+                 || reachable::find_reachable(tcx, &access_levels));
+
+        time(time_passes, "death checking", || {
+            middle::dead::check_crate(tcx, &access_levels);
+        });
+
+        let ref lib_features_used =
+            time(time_passes,
+                 "stability checking",
+                 || stability::check_unstable_api_usage(tcx));
+
+        time(time_passes, "unused lib feature checking", || {
+            stability::check_unused_or_stable_features(&tcx.sess,
+                                                       lib_features_used)
+        });
+
+        time(time_passes,
+             "lint checking",
+             || lint::check_crate(tcx, &access_levels));
+
+        // The above three passes generate errors w/o aborting
+        throw_if_errors!(tcx.sess);
+
+        Ok(f(tcx,
+          mir_map,
+          ty::CrateAnalysis {
+              export_map: export_map,
+              access_levels: access_levels,
+              reachable: reachable_map,
+              name: name,
+              glob_map: glob_map,
+          }))
+    })
 }
 
 /// Run the translation phase to LLVM, after which the AST and analysis can
@@ -893,7 +893,7 @@ pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>,
 /// as a side effect.
 pub fn phase_5_run_llvm_passes(sess: &Session,
                                trans: &trans::CrateTranslation,
-                               outputs: &OutputFilenames) {
+                               outputs: &OutputFilenames) -> CompileResult {
     if sess.opts.cg.no_integrated_as {
         let mut map = HashMap::new();
         map.insert(OutputType::Assembly, None);
@@ -913,7 +913,8 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
              || write::run_passes(sess, trans, &sess.opts.output_types, outputs));
     }
 
-    sess.abort_if_errors();
+    throw_if_errors!(sess);
+    Ok(())
 }
 
 /// Run the linker on any artifacts that resulted from the LLVM run.
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 31151e10a5a..030b29db3f2 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -104,49 +104,95 @@ pub mod target_features;
 const BUG_REPORT_URL: &'static str = "https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.\
                                       md#bug-reports";
 
+// Err(0) means compilation was stopped, but no errors were found.
+// This would be better as a dedicated enum, but using try! is so convenient.
+pub type CompileResult = Result<(), usize>;
+
+pub fn compile_result_from_err_count(err_count: usize) -> CompileResult {
+    if err_count == 0 {
+        Ok(())
+    } else {
+        Err(err_count)
+    }
+}
+
+#[inline]
+fn abort_msg(err_count: usize) -> String {
+    match err_count {
+        0 => "aborting with no errors (maybe a bug?)".to_owned(),
+        1 => "aborting due to previous error".to_owned(),
+        e => format!("aborting due to {} previous errors", e),
+    }
+}
+
+pub fn abort_on_err<T>(result: Result<T, usize>, sess: &Session) -> T {
+    match result {
+        Err(err_count) => {
+            sess.fatal(&abort_msg(err_count));
+        }
+        Ok(x) => x,
+    }
+}
+
 pub fn run(args: Vec<String>) -> isize {
-    monitor(move || run_compiler(&args, &mut RustcDefaultCalls));
+    monitor(move || {
+        let (result, session) = run_compiler(&args, &mut RustcDefaultCalls);
+        if let Err(err_count) = result {
+            if err_count > 0 {
+                match session {
+                    Some(sess) => sess.fatal(&abort_msg(err_count)),
+                    None => {
+                        let mut emitter =
+                            errors::emitter::BasicEmitter::stderr(errors::ColorConfig::Auto);
+                        emitter.emit(None, &abort_msg(err_count), None, errors::Level::Fatal);
+                        panic!(errors::FatalError);
+                    }
+                }
+            }
+        }
+    });
     0
 }
 
 // Parse args and run the compiler. This is the primary entry point for rustc.
 // See comments on CompilerCalls below for details about the callbacks argument.
-pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
-    macro_rules! do_or_return {($expr: expr) => {
+pub fn run_compiler<'a>(args: &[String],
+                        callbacks: &mut CompilerCalls<'a>)
+                        -> (CompileResult, Option<Session>) {
+    macro_rules! do_or_return {($expr: expr, $sess: expr) => {
         match $expr {
-            Compilation::Stop => return,
+            Compilation::Stop => return (Ok(()), $sess),
             Compilation::Continue => {}
         }
     }}
 
     let matches = match handle_options(args.to_vec()) {
         Some(matches) => matches,
-        None => return,
+        None => return (Ok(()), None),
     };
 
     let sopts = config::build_session_options(&matches);
 
     let descriptions = diagnostics_registry();
 
-    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format));
+    do_or_return!(callbacks.early_callback(&matches, &descriptions, sopts.error_format), None);
 
     let (odir, ofile) = make_output(&matches);
     let (input, input_file_path) = match make_input(&matches.free) {
         Some((input, input_file_path)) => callbacks.some_input(input, input_file_path),
         None => match callbacks.no_input(&matches, &sopts, &odir, &ofile, &descriptions) {
             Some((input, input_file_path)) => (input, input_file_path),
-            None => return,
+            None => return (Ok(()), None),
         },
     };
 
     let cstore = Rc::new(CStore::new(token::get_ident_interner()));
-    let sess = build_session(sopts, input_file_path, descriptions,
-                                 cstore.clone());
+    let sess = build_session(sopts, input_file_path, descriptions, cstore.clone());
     rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
     let mut cfg = config::build_configuration(&sess);
     target_features::add_configuration(&mut cfg, &sess);
 
-    do_or_return!(callbacks.late_callback(&matches, &sess, &input, &odir, &ofile));
+    do_or_return!(callbacks.late_callback(&matches, &sess, &input, &odir, &ofile), Some(sess));
 
     // It is somewhat unfortunate that this is hardwired in - this is forced by
     // the fact that pretty_print_input requires the session by value.
@@ -154,7 +200,7 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
     match pretty {
         Some((ppm, opt_uii)) => {
             pretty::pretty_print_input(sess, &cstore, cfg, &input, ppm, opt_uii, ofile);
-            return;
+            return (Ok(()), None);
         }
         None => {
             // continue
@@ -163,8 +209,9 @@ pub fn run_compiler<'a>(args: &[String], callbacks: &mut CompilerCalls<'a>) {
 
     let plugins = sess.opts.debugging_opts.extra_plugins.clone();
     let control = callbacks.build_controller(&sess);
-    driver::compile_input(sess, &cstore, cfg, &input, &odir, &ofile,
-                          Some(plugins), control);
+    (driver::compile_input(&sess, &cstore, cfg, &input, &odir, &ofile,
+                           Some(plugins), control),
+     Some(sess))
 }
 
 // Extract output directory and file from matches.
diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs
index ba5ecc22e74..1e99067bf32 100644
--- a/src/librustc_driver/pretty.rs
+++ b/src/librustc_driver/pretty.rs
@@ -17,7 +17,7 @@ use self::NodesMatchingUII::*;
 
 use rustc_trans::back::link;
 
-use driver;
+use {driver, abort_on_err};
 
 use rustc::middle::ty;
 use rustc::middle::cfg;
@@ -194,21 +194,21 @@ impl PpSourceMode {
                 f(&annotation, payload, &ast_map.forest.krate)
             }
             PpmTyped => {
-                driver::phase_3_run_analysis_passes(sess,
-                                                    cstore,
-                                                    ast_map.clone(),
-                                                    arenas,
-                                                    id,
-                                                    resolve::MakeGlobMap::No,
-                                                    |tcx, _, _| {
-                                                        let annotation = TypedAnnotation {
-                                                            tcx: tcx,
-                                                        };
-                                                        let _ignore = tcx.dep_graph.in_ignore();
-                                                        f(&annotation,
-                                                          payload,
-                                                          &ast_map.forest.krate)
-                                                    })
+                abort_on_err(driver::phase_3_run_analysis_passes(sess,
+                                                                 cstore,
+                                                                 ast_map.clone(),
+                                                                 arenas,
+                                                                 id,
+                                                                 resolve::MakeGlobMap::No,
+                                                                 |tcx, _, _| {
+                    let annotation = TypedAnnotation {
+                        tcx: tcx,
+                    };
+                    let _ignore = tcx.dep_graph.in_ignore();
+                    f(&annotation,
+                      payload,
+                      &ast_map.forest.krate)
+                }), sess)
             }
             _ => panic!("Should use call_with_pp_support"),
         }
@@ -694,8 +694,8 @@ pub fn pretty_print_input(sess: Session,
     let compute_ast_map = needs_ast_map(&ppm, &opt_uii);
     let krate = if compute_ast_map {
         match driver::phase_2_configure_and_expand(&sess, &cstore, krate, &id[..], None) {
-            None => return,
-            Some(k) => driver::assign_node_ids(&sess, k),
+            Err(_) => return,
+            Ok(k) => driver::assign_node_ids(&sess, k),
         }
     } else {
         krate
@@ -818,19 +818,19 @@ pub fn pretty_print_input(sess: Session,
             match code {
                 Some(code) => {
                     let variants = gather_flowgraph_variants(&sess);
-                    driver::phase_3_run_analysis_passes(&sess,
-                                                        &cstore,
-                                                        ast_map,
-                                                        &arenas,
-                                                        &id,
-                                                        resolve::MakeGlobMap::No,
-                                                        |tcx, _, _| {
-                                                            print_flowgraph(variants,
-                                                                            tcx,
-                                                                            code,
-                                                                            mode,
-                                                                            out)
-                                                        })
+                    abort_on_err(driver::phase_3_run_analysis_passes(&sess,
+                                                                     &cstore,
+                                                                     ast_map,
+                                                                     &arenas,
+                                                                     &id,
+                                                                     resolve::MakeGlobMap::No,
+                                                                     |tcx, _, _| {
+                        print_flowgraph(variants,
+                                        tcx,
+                                        code,
+                                        mode,
+                                        out)
+                    }), &sess)
                 }
                 None => {
                     let message = format!("--pretty=flowgraph needs block, fn, or method; got \
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index a7fd170b91c..5eac358f1bf 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -10,7 +10,7 @@
 pub use self::MaybeTyped::*;
 
 use rustc_lint;
-use rustc_driver::{driver, target_features};
+use rustc_driver::{driver, target_features, abort_on_err};
 use rustc::session::{self, config};
 use rustc::middle::def_id::DefId;
 use rustc::middle::privacy::AccessLevels;
@@ -147,13 +147,13 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
     let arenas = ty::CtxtArenas::new();
     let hir_map = driver::make_map(&sess, &mut hir_forest);
 
-    driver::phase_3_run_analysis_passes(&sess,
-                                        &cstore,
-                                        hir_map,
-                                        &arenas,
-                                        &name,
-                                        resolve::MakeGlobMap::No,
-                                        |tcx, _, analysis| {
+    abort_on_err(driver::phase_3_run_analysis_passes(&sess,
+                                                     &cstore,
+                                                     hir_map,
+                                                     &arenas,
+                                                     &name,
+                                                     resolve::MakeGlobMap::No,
+                                                     |tcx, _, analysis| {
         let _ignore = tcx.dep_graph.in_ignore();
         let ty::CrateAnalysis { access_levels, .. } = analysis;
 
@@ -200,5 +200,5 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec<String>, externs: Externs,
         *analysis.inlined.borrow_mut() = map;
         analysis.deref_trait_did = ctxt.deref_trait_did.get();
         (krate, analysis)
-    })
+    }), &sess)
 }
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index de117b44b6f..81f984a5927 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -250,7 +250,12 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec<String>, libs: SearchPaths,
     if no_run {
         control.after_analysis.stop = Compilation::Stop;
     }
-    driver::compile_input(sess, &cstore, cfg, &input, &out, &None, None, control);
+    let result = driver::compile_input(&sess, &cstore, cfg, &input,
+                                       &out, &None, None, control);
+    match result {
+        Err(count) if count > 0 => sess.fatal("aborting due to previous error(s)"),
+        _ => {}
+    }
 
     if no_run { return }
 
diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs
index dc409f393a8..fe6a5faf932 100644
--- a/src/test/run-make/execution-engine/test.rs
+++ b/src/test/run-make/execution-engine/test.rs
@@ -32,7 +32,7 @@ use rustc::middle::cstore::{CrateStore, LinkagePreference};
 use rustc::middle::ty;
 use rustc::session::config::{self, basic_options, build_configuration, Input, Options};
 use rustc::session::build_session;
-use rustc_driver::driver;
+use rustc_driver::{driver, abort_on_err};
 use rustc_front::lowering::{lower_crate, LoweringContext};
 use rustc_resolve::MakeGlobMap;
 use rustc_metadata::cstore::CStore;
@@ -234,7 +234,7 @@ fn compile_program(input: &str, sysroot: PathBuf)
         let arenas = ty::CtxtArenas::new();
         let ast_map = driver::make_map(&sess, &mut hir_forest);
 
-        driver::phase_3_run_analysis_passes(
+        abort_on_err(driver::phase_3_run_analysis_passes(
             &sess, &cstore, ast_map, &arenas, &id,
             MakeGlobMap::No, |tcx, mir_map, analysis| {
 
@@ -254,7 +254,7 @@ fn compile_program(input: &str, sysroot: PathBuf)
             let modp = llmod as usize;
 
             (modp, deps)
-        })
+        }), &sess)
     }).unwrap();
 
     match handle.join() {
diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs
index 4b1c84ce64f..b2aed915458 100644
--- a/src/test/run-make/issue-19371/foo.rs
+++ b/src/test/run-make/issue-19371/foo.rs
@@ -65,7 +65,7 @@ fn compile(code: String, output: PathBuf, sysroot: PathBuf) {
     let cfg = build_configuration(&sess);
     let control = CompileController::basic();
 
-    compile_input(sess, &cstore,
+    compile_input(&sess, &cstore,
             cfg,
             &Input::Str(code),
             &None,