Auto merge of #31206 - nrc:early-save, r=nikomatsakis

r? @nikomatsakis
This commit is contained in:
bors 2016-01-27 10:17:55 +00:00
commit b8b18aac12
8 changed files with 308 additions and 242 deletions

View File

@ -176,15 +176,28 @@ impl Session {
pub fn abort_if_errors(&self) {
self.diagnostic().abort_if_errors();
}
pub fn track_errors<F, T>(&self, f: F) -> Result<T, usize>
where F: FnOnce() -> T
{
let mut count = self.err_count();
let result = f();
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
{
let count = self.err_count();
let result = f();
if self.err_count() > count {
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)

View File

@ -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));
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,
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) {
None => return,
Some(k) => k,
};
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,14 +158,13 @@ pub fn compile_input(sess: Session,
None
};
phase_3_run_analysis_passes(&sess,
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,
@ -175,9 +179,9 @@ pub fn compile_input(sess: Session,
&id);
(control.after_analysis.callback)(state);
tcx.sess.abort_if_errors();
throw_if_errors!(tcx.sess);
if control.after_analysis.stop == Compilation::Stop {
return Err(());
return Err(0usize);
}
}
@ -198,22 +202,18 @@ pub fn compile_input(sess: Session,
token::get_ident_interner().clear();
Ok((outputs, trans))
})
})))
};
let (outputs, trans) = if let Ok(out) = result {
out
} else {
return;
};
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,
@ -827,7 +827,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
// 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();
throw_if_errors!(tcx.sess);
let reachable_map =
time(time_passes,
@ -853,9 +853,9 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
|| lint::check_crate(tcx, &access_levels));
// The above three passes generate errors w/o aborting
tcx.sess.abort_if_errors();
throw_if_errors!(tcx.sess);
f(tcx,
Ok(f(tcx,
mir_map,
ty::CrateAnalysis {
export_map: export_map,
@ -863,7 +863,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
reachable: reachable_map,
name: name,
glob_map: glob_map,
})
}))
})
}
@ -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.

View File

@ -105,49 +105,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.
@ -155,7 +201,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
@ -164,8 +210,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.

View File

@ -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,7 +194,7 @@ impl PpSourceMode {
f(&annotation, payload, &ast_map.forest.krate)
}
PpmTyped => {
driver::phase_3_run_analysis_passes(sess,
abort_on_err(driver::phase_3_run_analysis_passes(sess,
cstore,
ast_map.clone(),
arenas,
@ -208,7 +208,7 @@ impl PpSourceMode {
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,7 +818,7 @@ pub fn pretty_print_input(sess: Session,
match code {
Some(code) => {
let variants = gather_flowgraph_variants(&sess);
driver::phase_3_run_analysis_passes(&sess,
abort_on_err(driver::phase_3_run_analysis_passes(&sess,
&cstore,
ast_map,
&arenas,
@ -830,7 +830,7 @@ pub fn pretty_print_input(sess: Session,
code,
mode,
out)
})
}), &sess)
}
None => {
let message = format!("--pretty=flowgraph needs block, fn, or method; got \

View File

@ -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,7 +147,7 @@ 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,
abort_on_err(driver::phase_3_run_analysis_passes(&sess,
&cstore,
hir_map,
&arenas,
@ -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)
}

View File

@ -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 }

View File

@ -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() {

View File

@ -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,