From 004533ea755ecfc5d65e282366aaffc523e9632c Mon Sep 17 00:00:00 2001 From: Murarth Date: Fri, 28 Nov 2014 21:56:09 -0700 Subject: [PATCH] Fix rustc panic on second compile_input --- src/librustc_trans/driver/driver.rs | 6 +++ src/libsyntax/ext/mtwt.rs | 10 +++++ src/libsyntax/parse/token.rs | 6 +++ src/libsyntax/util/interner.rs | 5 +++ src/test/run-make/issue-19371/Makefile | 9 ++++ src/test/run-make/issue-19371/foo.rs | 62 ++++++++++++++++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 src/test/run-make/issue-19371/Makefile create mode 100644 src/test/run-make/issue-19371/foo.rs diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs index a5709a0219b..aeef16276e5 100644 --- a/src/librustc_trans/driver/driver.rs +++ b/src/librustc_trans/driver/driver.rs @@ -51,6 +51,12 @@ pub fn compile_input(sess: Session, outdir: &Option, output: &Option, addl_plugins: Option) { + // These may be left in an incoherent state after a previous compile. + // `clear_tables` and `get_ident_interner().clear()` can be used to free + // memory, but they do not restore the initial state. + syntax::ext::mtwt::reset_tables(); + token::reset_ident_interner(); + // 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 diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs index e6d886e28ba..6ba90bbebed 100644 --- a/src/libsyntax/ext/mtwt.rs +++ b/src/libsyntax/ext/mtwt.rs @@ -136,6 +136,16 @@ pub fn clear_tables() { with_resolve_table_mut(|table| *table = HashMap::new()); } +/// Reset the tables to their initial state +pub fn reset_tables() { + with_sctable(|table| { + *table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt); + *table.mark_memo.borrow_mut() = HashMap::new(); + *table.rename_memo.borrow_mut() = HashMap::new(); + }); + with_resolve_table_mut(|table| *table = HashMap::new()); +} + /// Add a value to the end of a vec, return its index fn idx_push(vec: &mut Vec, val: T) -> u32 { vec.push(val); diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 583ace977fe..37df2bf14c2 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -564,6 +564,12 @@ pub fn get_ident_interner() -> Rc { KEY.with(|k| k.clone()) } +/// Reset the ident interner to its initial state. +pub fn reset_ident_interner() { + let interner = get_ident_interner(); + interner.reset(mk_fresh_ident_interner()); +} + /// Represents a string stored in the task-local interner. Because the /// interner lives for the life of the task, this can be safely treated as an /// immortal string, as long as it never crosses between tasks. diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs index ede967bba25..590a04ce221 100644 --- a/src/libsyntax/util/interner.rs +++ b/src/libsyntax/util/interner.rs @@ -214,6 +214,11 @@ impl StrInterner { *self.map.borrow_mut() = HashMap::new(); *self.vect.borrow_mut() = Vec::new(); } + + pub fn reset(&self, other: StrInterner) { + *self.map.borrow_mut() = other.map.into_inner(); + *self.vect.borrow_mut() = other.vect.into_inner(); + } } #[cfg(test)] diff --git a/src/test/run-make/issue-19371/Makefile b/src/test/run-make/issue-19371/Makefile new file mode 100644 index 00000000000..9f3ec78465b --- /dev/null +++ b/src/test/run-make/issue-19371/Makefile @@ -0,0 +1,9 @@ +-include ../tools.mk + +# This test ensures that rustc compile_input can be called twice in one task +# without causing a panic. +# The program needs the path to rustc to get sysroot. + +all: + $(RUSTC) foo.rs + $(call RUN,foo $(TMPDIR) $(RUSTC)) diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs new file mode 100644 index 00000000000..715fae314b6 --- /dev/null +++ b/src/test/run-make/issue-19371/foo.rs @@ -0,0 +1,62 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate rustc; +extern crate rustc_trans; +extern crate syntax; + +use rustc::session::{build_session, Session}; +use rustc::session::config::{basic_options, build_configuration, OutputTypeExe}; +use rustc_trans::driver::driver::{Input, StrInput, compile_input}; +use syntax::diagnostics::registry::Registry; + +fn main() { + let src = r#" + fn main() {} + "#; + + let args = std::os::args(); + + if args.len() < 4 { + panic!("expected rustc path"); + } + + let tmpdir = Path::new(args[1].as_slice()); + + let mut sysroot = Path::new(args[3].as_slice()); + sysroot.pop(); + sysroot.pop(); + + compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); + + compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); +} + +fn basic_sess(sysroot: Path) -> Session { + let mut opts = basic_options(); + opts.output_types = vec![OutputTypeExe]; + opts.maybe_sysroot = Some(sysroot); + + let descriptions = Registry::new(&rustc::DIAGNOSTICS); + let sess = build_session(opts, None, descriptions); + sess +} + +fn compile(code: String, output: Path, sysroot: Path) { + let sess = basic_sess(sysroot); + let cfg = build_configuration(&sess); + + compile_input(sess, + cfg, + &StrInput(code), + &None, + &Some(output), + None); +}