drop(fs::remove_dir_all(&dst));
build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));
- let mut configure = Command::new(obj.join("Configure"));
+ let mut configure = Command::new("perl");
+ configure.arg(obj.join("Configure"));
configure.arg(format!("--prefix={}", dst.display()));
configure.arg("no-dso");
configure.arg("no-ssl2");
//! get confused if the spans from leaf AST nodes occur in multiple places
//! in the HIR, especially for multiple identifiers.
+use dep_graph::DepGraph;
use hir;
use hir::map::{Definitions, DefKey};
use hir::def_id::{DefIndex, DefId, CRATE_DEF_INDEX};
pub fn lower_crate(sess: &Session,
cstore: &CrateStore,
+ dep_graph: &DepGraph,
krate: &Crate,
resolver: &mut Resolver)
-> hir::Crate {
// We're constructing the HIR here; we don't care what we will
// read, since we haven't even constructed the *input* to
// incr. comp. yet.
- let _ignore = sess.dep_graph.in_ignore();
+ let _ignore = dep_graph.in_ignore();
LoweringContext {
crate_root: std_inject::injected_crate_name(krate),
#[cfg(test)]
mod tests {
- use dep_graph::DepGraph;
use errors;
use getopts;
use lint;
// When the user supplies --test we should implicitly supply --cfg test
#[test]
fn test_switch_implies_cfg_test() {
- let dep_graph = DepGraph::new(false);
let matches =
&match optgroups().parse(&["--test".to_string()]) {
Ok(m) => m,
};
let registry = errors::registry::Registry::new(&[]);
let (sessopts, cfg) = build_session_options_and_crate_config(matches);
- let sess = build_session(sessopts, &dep_graph, None, registry);
+ let sess = build_session(sessopts, None, registry);
let cfg = build_configuration(&sess, cfg);
assert!(cfg.contains(&(Symbol::intern("test"), None)));
}
// another --cfg test
#[test]
fn test_switch_implies_cfg_test_unless_cfg_test() {
- let dep_graph = DepGraph::new(false);
let matches =
&match optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]) {
Ok(m) => m,
};
let registry = errors::registry::Registry::new(&[]);
let (sessopts, cfg) = build_session_options_and_crate_config(matches);
- let sess = build_session(sessopts, &dep_graph, None, registry);
+ let sess = build_session(sessopts, None, registry);
let cfg = build_configuration(&sess, cfg);
let mut test_items = cfg.iter().filter(|&&(name, _)| name == "test");
assert!(test_items.next().is_some());
#[test]
fn test_can_print_warnings() {
- let dep_graph = DepGraph::new(false);
{
let matches = optgroups().parse(&[
"-Awarnings".to_string()
]).unwrap();
let registry = errors::registry::Registry::new(&[]);
let (sessopts, _) = build_session_options_and_crate_config(&matches);
- let sess = build_session(sessopts, &dep_graph, None, registry);
+ let sess = build_session(sessopts, None, registry);
assert!(!sess.diagnostic().can_emit_warnings);
}
]).unwrap();
let registry = errors::registry::Registry::new(&[]);
let (sessopts, _) = build_session_options_and_crate_config(&matches);
- let sess = build_session(sessopts, &dep_graph, None, registry);
+ let sess = build_session(sessopts, None, registry);
assert!(sess.diagnostic().can_emit_warnings);
}
]).unwrap();
let registry = errors::registry::Registry::new(&[]);
let (sessopts, _) = build_session_options_and_crate_config(&matches);
- let sess = build_session(sessopts, &dep_graph, None, registry);
+ let sess = build_session(sessopts, None, registry);
assert!(sess.diagnostic().can_emit_warnings);
}
}
pub use self::code_stats::{CodeStats, DataTypeKind, FieldInfo};
pub use self::code_stats::{SizeKind, TypeSizeInfo, VariantInfo};
-use dep_graph::DepGraph;
use hir::def_id::{CrateNum, DefIndex};
use lint;
// Represents the data associated with a compilation
// session for a single crate.
pub struct Session {
- pub dep_graph: DepGraph,
pub target: config::Config,
pub host: Target,
pub opts: config::Options,
// forms a unique global identifier for the crate. It is used to allow
// multiple crates with the same name to coexist. See the
// trans::back::symbol_names module for more information.
- pub crate_disambiguator: RefCell<Symbol>,
+ pub crate_disambiguator: RefCell<Option<Symbol>>,
pub features: RefCell<feature_gate::Features>,
/// The maximum recursion limit for potentially infinitely recursive
impl Session {
pub fn local_crate_disambiguator(&self) -> Symbol {
- *self.crate_disambiguator.borrow()
+ match *self.crate_disambiguator.borrow() {
+ Some(sym) => sym,
+ None => bug!("accessing disambiguator before initialization"),
+ }
}
pub fn struct_span_warn<'a, S: Into<MultiSpan>>(&'a self,
sp: S,
kind)
}
+ pub fn set_incr_session_load_dep_graph(&self, load: bool) {
+ let mut incr_comp_session = self.incr_comp_session.borrow_mut();
+
+ match *incr_comp_session {
+ IncrCompSession::Active { ref mut load_dep_graph, .. } => {
+ *load_dep_graph = load;
+ }
+ _ => {}
+ }
+ }
+
+ pub fn incr_session_load_dep_graph(&self) -> bool {
+ let incr_comp_session = self.incr_comp_session.borrow();
+ match *incr_comp_session {
+ IncrCompSession::Active { load_dep_graph, .. } => load_dep_graph,
+ _ => false,
+ }
+ }
+
pub fn init_incr_comp_session(&self,
session_dir: PathBuf,
- lock_file: flock::Lock) {
+ lock_file: flock::Lock,
+ load_dep_graph: bool) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
if let IncrCompSession::NotInitialized = *incr_comp_session { } else {
*incr_comp_session = IncrCompSession::Active {
session_directory: session_dir,
lock_file,
+ load_dep_graph,
};
}
}
pub fn build_session(sopts: config::Options,
- dep_graph: &DepGraph,
local_crate_source_file: Option<PathBuf>,
registry: errors::registry::Registry)
-> Session {
let file_path_mapping = sopts.file_path_mapping();
build_session_with_codemap(sopts,
- dep_graph,
local_crate_source_file,
registry,
Rc::new(codemap::CodeMap::new(file_path_mapping)),
}
pub fn build_session_with_codemap(sopts: config::Options,
- dep_graph: &DepGraph,
local_crate_source_file: Option<PathBuf>,
registry: errors::registry::Registry,
codemap: Rc<codemap::CodeMap>,
emitter);
build_session_(sopts,
- dep_graph,
local_crate_source_file,
diagnostic_handler,
codemap)
}
pub fn build_session_(sopts: config::Options,
- dep_graph: &DepGraph,
local_crate_source_file: Option<PathBuf>,
span_diagnostic: errors::Handler,
codemap: Rc<codemap::CodeMap>)
let working_dir = file_path_mapping.map_prefix(working_dir);
let sess = Session {
- dep_graph: dep_graph.clone(),
target: target_cfg,
host,
opts: sopts,
plugin_attributes: RefCell::new(Vec::new()),
crate_types: RefCell::new(Vec::new()),
dependency_formats: RefCell::new(FxHashMap()),
- crate_disambiguator: RefCell::new(Symbol::intern("")),
+ crate_disambiguator: RefCell::new(None),
features: RefCell::new(feature_gate::Features::new()),
recursion_limit: Cell::new(64),
type_length_limit: Cell::new(1048576),
Active {
session_directory: PathBuf,
lock_file: flock::Lock,
+ load_dep_graph: bool,
},
// This is the state after the session directory has been finalized. In this
// state, the contents of the directory must not be modified any more.
fn join(&self, pred1: usize, pred2: usize) -> usize;
}
+pub struct Intersect;
+impl BitwiseOperator for Intersect {
+ #[inline]
+ fn join(&self, a: usize, b: usize) -> usize { a & b }
+}
pub struct Union;
impl BitwiseOperator for Union {
#[inline]
use std::ops::{Deref, DerefMut, Range};
use std::slice;
use bitslice::{BitSlice, Word};
-use bitslice::{bitwise, Union, Subtract};
+use bitslice::{bitwise, Union, Subtract, Intersect};
use indexed_vec::Idx;
/// Represents a set (or packed family of sets), of some element type
bitwise(self.words_mut(), other.words(), &Subtract)
}
+ pub fn intersect(&mut self, other: &IdxSet<T>) -> bool {
+ bitwise(self.words_mut(), other.words(), &Intersect)
+ }
+
pub fn iter(&self) -> Iter<T> {
Iter {
cur: None,
#![cfg_attr(not(feature="llvm"), allow(dead_code))]
+use rustc::dep_graph::DepGraph;
use rustc::hir::{self, map as hir_map};
use rustc::hir::lowering::lower_crate;
use rustc::ich::Fingerprint;
// 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 (outputs, trans): (OutputFilenames, OngoingCrateTranslation) = {
+ let (outputs, trans, dep_graph): (OutputFilenames, OngoingCrateTranslation, DepGraph) = {
let krate = match phase_1_parse_input(control, sess, input) {
Ok(krate) => krate,
Err(mut parse_error) => {
::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input);
let ExpansionResult { expanded_crate, defs, analysis, resolutions, mut hir_forest } = {
phase_2_configure_and_expand(
- sess, &cstore, krate, registry, &crate_name, addl_plugins, control.make_glob_map,
+ sess,
+ &cstore,
+ krate,
+ registry,
+ &crate_name,
+ addl_plugins,
+ control.make_glob_map,
|expanded_crate| {
let mut state = CompileState::state_after_expand(
input, sess, outdir, output, &cstore, expanded_crate, &crate_name,
}
}
- Ok((outputs, trans))
+ Ok((outputs, trans, tcx.dep_graph.clone()))
})??
};
sess.code_stats.borrow().print_type_sizes();
}
- let (phase5_result, trans) = phase_5_run_llvm_passes(sess, trans);
+ let (phase5_result, trans) = phase_5_run_llvm_passes(sess, &dep_graph, trans);
controller_entry_point!(after_llvm,
sess,
*sess.features.borrow_mut() = features;
*sess.crate_types.borrow_mut() = collect_crate_types(sess, &krate.attrs);
- *sess.crate_disambiguator.borrow_mut() = Symbol::intern(&compute_crate_disambiguator(sess));
+
+ let disambiguator = Symbol::intern(&compute_crate_disambiguator(sess));
+ *sess.crate_disambiguator.borrow_mut() = Some(disambiguator);
+ rustc_incremental::prepare_session_directory(
+ sess,
+ &crate_name,
+ &disambiguator.as_str(),
+ );
+ let dep_graph = DepGraph::new(sess.opts.build_dep_graph());
time(time_passes, "recursion limit", || {
middle::recursion_limit::update_limits(sess, &krate);
// item, much like we do for macro expansion. In other words, the hash reflects not just
// its contents but the results of name resolution on those contents. Hopefully we'll push
// this back at some point.
- let _ignore = sess.dep_graph.in_ignore();
+ let _ignore = dep_graph.in_ignore();
let mut crate_loader = CrateLoader::new(sess, &cstore, crate_name);
let resolver_arenas = Resolver::arenas();
let mut resolver = Resolver::new(sess,
// Lower ast -> hir.
let hir_forest = time(time_passes, "lowering ast -> hir", || {
- let hir_crate = lower_crate(sess, cstore, &krate, &mut resolver);
+ let hir_crate = lower_crate(sess, cstore, &dep_graph, &krate, &mut resolver);
if sess.opts.debugging_opts.hir_stats {
hir_stats::print_hir_stats(&hir_crate);
}
- hir_map::Forest::new(hir_crate, &sess.dep_graph)
+ hir_map::Forest::new(hir_crate, &dep_graph)
});
time(time_passes,
/// as a side effect.
#[cfg(feature="llvm")]
pub fn phase_5_run_llvm_passes(sess: &Session,
+ dep_graph: &DepGraph,
trans: write::OngoingCrateTranslation)
-> (CompileResult, trans::CrateTranslation) {
- let trans = trans.join(sess);
+ let trans = trans.join(sess, dep_graph);
if sess.opts.debugging_opts.incremental_info {
write::dump_incremental_data(&trans);
time(sess.time_passes(),
"serialize work products",
- move || rustc_incremental::save_work_products(sess));
+ move || rustc_incremental::save_work_products(sess, dep_graph));
(sess.compile_status(), trans)
}
use rustc_resolve as resolve;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
-use rustc::dep_graph::DepGraph;
use rustc::session::{self, config, Session, build_session, CompileResult};
use rustc::session::CompileIncomplete;
use rustc::session::config::{Input, PrintRequest, OutputType, ErrorOutputType};
},
};
- let dep_graph = DepGraph::new(sopts.build_dep_graph());
let cstore = Rc::new(CStore::new(box ::MetadataLoader));
let loader = file_loader.unwrap_or(box RealFileLoader);
let codemap = Rc::new(CodeMap::with_file_loader(loader, sopts.file_path_mapping()));
let mut sess = session::build_session_with_codemap(
- sopts, &dep_graph, input_file_path, descriptions, codemap, emitter_dest,
+ sopts, input_file_path, descriptions, codemap, emitter_dest,
);
rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let plugins = sess.opts.debugging_opts.extra_plugins.clone();
let control = callbacks.build_controller(&sess, &matches);
- (driver::compile_input(&sess, &cstore, &input, &odir, &ofile, Some(plugins), &control),
+ (driver::compile_input(&sess,
+ &cstore,
+ &input,
+ &odir,
+ &ofile,
+ Some(plugins),
+ &control),
Some(sess))
}
describe_lints(&ls, false);
return None;
}
- let dep_graph = DepGraph::new(sopts.build_dep_graph());
let mut sess = build_session(sopts.clone(),
- &dep_graph,
None,
descriptions.clone());
rustc_trans::init(&sess);
//! # Standalone Tests for the Inference Module
use driver;
-use rustc::dep_graph::DepGraph;
use rustc_lint;
use rustc_resolve::MakeGlobMap;
use rustc_trans;
options.unstable_features = UnstableFeatures::Allow;
let diagnostic_handler = errors::Handler::with_emitter(true, false, emitter);
- let dep_graph = DepGraph::new(false);
- let _ignore = dep_graph.in_ignore();
let cstore = Rc::new(CStore::new(box ::MetadataLoader));
let sess = session::build_session_(options,
- &dep_graph,
None,
diagnostic_handler,
Rc::new(CodeMap::new(FilePathMapping::empty())));
|_| Ok(()))
.expect("phase 2 aborted")
};
- let _ignore = dep_graph.in_ignore();
let arena = DroplessArena::new();
let arenas = ty::GlobalArenas::new();
pub use persist::save_trans_partition;
pub use persist::save_work_products;
pub use persist::in_incr_comp_dir;
+pub use persist::prepare_session_directory;
pub use persist::finalize_session_directory;
//! unsupported file system and emit a warning in that case. This is not yet
//! implemented.
-use rustc::hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc::hir::def_id::CrateNum;
use rustc::hir::svh::Svh;
use rustc::session::Session;
use rustc::ty::TyCtxt;
/// a dep-graph and work products from a previous session.
/// If the call fails, the fn may leave behind an invalid session directory.
/// The garbage collection will take care of it.
-pub fn prepare_session_directory(tcx: TyCtxt) -> Result<bool, ()> {
+pub fn prepare_session_directory(sess: &Session,
+ crate_name: &str,
+ crate_disambiguator: &str) {
+ if sess.opts.incremental.is_none() {
+ return
+ }
+
debug!("prepare_session_directory");
// {incr-comp-dir}/{crate-name-and-disambiguator}
- let crate_dir = crate_path_tcx(tcx, LOCAL_CRATE);
+ let crate_dir = crate_path(sess, crate_name, crate_disambiguator);
debug!("crate-dir: {}", crate_dir.display());
- try!(create_dir(tcx.sess, &crate_dir, "crate"));
+ if create_dir(sess, &crate_dir, "crate").is_err() {
+ return
+ }
// Hack: canonicalize the path *after creating the directory*
// because, on windows, long paths can cause problems;
let crate_dir = match crate_dir.canonicalize() {
Ok(v) => v,
Err(err) => {
- tcx.sess.err(&format!("incremental compilation: error canonicalizing path `{}`: {}",
- crate_dir.display(), err));
- return Err(());
+ sess.err(&format!("incremental compilation: error canonicalizing path `{}`: {}",
+ crate_dir.display(), err));
+ return
}
};
// Lock the new session directory. If this fails, return an
// error without retrying
- let (directory_lock, lock_file_path) = try!(lock_directory(tcx.sess, &session_dir));
+ let (directory_lock, lock_file_path) = match lock_directory(sess, &session_dir) {
+ Ok(e) => e,
+ Err(_) => return,
+ };
// Now that we have the lock, we can actually create the session
// directory
- try!(create_dir(tcx.sess, &session_dir, "session"));
+ if create_dir(sess, &session_dir, "session").is_err() {
+ return
+ }
// Find a suitable source directory to copy from. Ignore those that we
// have already tried before.
debug!("no source directory found. Continuing with empty session \
directory.");
- tcx.sess.init_incr_comp_session(session_dir, directory_lock);
- return Ok(false)
+ sess.init_incr_comp_session(session_dir, directory_lock, false);
+ return
};
debug!("attempting to copy data from source: {}",
source_directory.display());
- let print_file_copy_stats = tcx.sess.opts.debugging_opts.incremental_info;
+ let print_file_copy_stats = sess.opts.debugging_opts.incremental_info;
// Try copying over all files from the source directory
if let Ok(allows_links) = copy_files(&session_dir, &source_directory,
source_directory.display());
if !allows_links {
- tcx.sess.warn(&format!("Hard linking files in the incremental \
+ sess.warn(&format!("Hard linking files in the incremental \
compilation cache failed. Copying files \
instead. Consider moving the cache \
directory to a file system which supports \
);
}
- tcx.sess.init_incr_comp_session(session_dir, directory_lock);
- return Ok(true)
+ sess.init_incr_comp_session(session_dir, directory_lock, true);
+ return
} else {
debug!("copying failed - trying next directory");
// Try to remove the session directory we just allocated. We don't
// know if there's any garbage in it from the failed copy action.
if let Err(err) = safe_remove_dir_all(&session_dir) {
- tcx.sess.warn(&format!("Failed to delete partly initialized \
- session dir `{}`: {}",
- session_dir.display(),
- err));
+ sess.warn(&format!("Failed to delete partly initialized \
+ session dir `{}`: {}",
+ session_dir.display(),
+ err));
}
- delete_session_dir_lock_file(tcx.sess, &lock_file_path);
+ delete_session_dir_lock_file(sess, &lock_file_path);
mem::drop(directory_lock);
}
}
/// more general overview.
pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
incremental_hashes_map: &IncrementalHashesMap) {
- if tcx.sess.opts.incremental.is_none() {
- return;
+ if tcx.sess.incr_session_load_dep_graph() {
+ let _ignore = tcx.dep_graph.in_ignore();
+ load_dep_graph_if_exists(tcx, incremental_hashes_map);
}
-
- match prepare_session_directory(tcx) {
- Ok(true) => {
- // We successfully allocated a session directory and there is
- // something in it to load, so continue
- }
- Ok(false) => {
- // We successfully allocated a session directory, but there is no
- // dep-graph data in it to load (because this is the first
- // compilation session with this incr. comp. dir.)
- return
- }
- Err(()) => {
- // Something went wrong while trying to allocate the session
- // directory. Don't try to use it any further.
- return
- }
- }
-
- let _ignore = tcx.dep_graph.in_ignore();
- load_dep_graph_if_exists(tcx, incremental_hashes_map);
}
fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mod work_product;
mod file_format;
+pub use self::fs::prepare_session_directory;
pub use self::fs::finalize_session_directory;
pub use self::fs::in_incr_comp_dir;
pub use self::load::load_dep_graph;
// option. This file may not be copied, modified, or distributed
// except according to those terms.
-use rustc::dep_graph::DepNode;
+use rustc::dep_graph::{DepGraph, DepNode};
use rustc::hir::def_id::DefId;
use rustc::hir::svh::Svh;
use rustc::ich::Fingerprint;
¤t_metadata_hashes);
}
-pub fn save_work_products(sess: &Session) {
+pub fn save_work_products(sess: &Session, dep_graph: &DepGraph) {
if sess.opts.incremental.is_none() {
return;
}
debug!("save_work_products()");
- let _ignore = sess.dep_graph.in_ignore();
+ let _ignore = dep_graph.in_ignore();
let path = work_products_path(sess);
- save_in(sess, path, |e| encode_work_products(sess, e));
+ save_in(sess, path, |e| encode_work_products(dep_graph, e));
// We also need to clean out old work-products, as not all of them are
// deleted during invalidation. Some object files don't change their
// content, they are just not needed anymore.
- let new_work_products = sess.dep_graph.work_products();
- let previous_work_products = sess.dep_graph.previous_work_products();
+ let new_work_products = dep_graph.work_products();
+ let previous_work_products = dep_graph.previous_work_products();
for (id, wp) in previous_work_products.iter() {
if !new_work_products.contains_key(id) {
Ok(())
}
-pub fn encode_work_products(sess: &Session, encoder: &mut Encoder) -> io::Result<()> {
- let work_products: Vec<_> = sess.dep_graph
+pub fn encode_work_products(dep_graph: &DepGraph,
+ encoder: &mut Encoder) -> io::Result<()> {
+ let work_products: Vec<_> = dep_graph
.work_products()
.iter()
.map(|(id, work_product)| {
//! This module contains files for saving intermediate work-products.
use persist::fs::*;
-use rustc::dep_graph::{WorkProduct, WorkProductId};
+use rustc::dep_graph::{WorkProduct, WorkProductId, DepGraph};
use rustc::session::Session;
use rustc::session::config::OutputType;
use rustc::util::fs::link_or_copy;
use std::fs as std_fs;
pub fn save_trans_partition(sess: &Session,
+ dep_graph: &DepGraph,
cgu_name: &str,
partition_hash: u64,
files: &[(OutputType, PathBuf)]) {
saved_files,
};
- sess.dep_graph.insert_work_product(&work_product_id, work_product);
+ dep_graph.insert_work_product(&work_product_id, work_product);
}
pub fn delete_workproduct_files(sess: &Session, work_product: &WorkProduct) {
use super::drop_flag_effects_for_location;
use super::on_lookup_result_bits;
+mod storage_liveness;
+
+pub use self::storage_liveness::*;
+
#[allow(dead_code)]
pub(super) mod borrows;
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+pub use super::*;
+
+use rustc::mir::*;
+use dataflow::BitDenotation;
+
+#[derive(Copy, Clone)]
+pub struct MaybeStorageLive<'a, 'tcx: 'a> {
+ mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx: 'a> MaybeStorageLive<'a, 'tcx> {
+ pub fn new(mir: &'a Mir<'tcx>)
+ -> Self {
+ MaybeStorageLive { mir: mir }
+ }
+
+ pub fn mir(&self) -> &Mir<'tcx> {
+ self.mir
+ }
+}
+
+impl<'a, 'tcx> BitDenotation for MaybeStorageLive<'a, 'tcx> {
+ type Idx = Local;
+ fn name() -> &'static str { "maybe_storage_live" }
+ fn bits_per_block(&self) -> usize {
+ self.mir.local_decls.len()
+ }
+
+ fn start_block_effect(&self, _sets: &mut BlockSets<Local>) {
+ // Nothing is live on function entry
+ }
+
+ fn statement_effect(&self,
+ sets: &mut BlockSets<Local>,
+ loc: Location) {
+ let stmt = &self.mir[loc.block].statements[loc.statement_index];
+
+ match stmt.kind {
+ StatementKind::StorageLive(l) => sets.gen(&l),
+ StatementKind::StorageDead(l) => sets.kill(&l),
+ _ => (),
+ }
+ }
+
+ fn terminator_effect(&self,
+ _sets: &mut BlockSets<Local>,
+ _loc: Location) {
+ // Terminators have no effect
+ }
+
+ fn propagate_call_return(&self,
+ _in_out: &mut IdxSet<Local>,
+ _call_bb: mir::BasicBlock,
+ _dest_bb: mir::BasicBlock,
+ _dest_lval: &mir::Lvalue) {
+ // Nothing to do when a call returns successfully
+ }
+}
+
+impl<'a, 'tcx> BitwiseOperator for MaybeStorageLive<'a, 'tcx> {
+ #[inline]
+ fn join(&self, pred1: usize, pred2: usize) -> usize {
+ pred1 | pred2 // "maybe" means we union effects of both preds
+ }
+}
+
+impl<'a, 'tcx> DataflowOperator for MaybeStorageLive<'a, 'tcx> {
+ #[inline]
+ fn bottom_value() -> bool {
+ false // bottom = dead
+ }
+}
use std::path::PathBuf;
use std::usize;
+pub use self::impls::{MaybeStorageLive};
pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals};
pub use self::impls::{DefinitelyInitializedLvals};
pub use self::impls::borrows::{Borrows, BorrowData, BorrowIndex};
flow_state: &mut Self::FlowState);
}
+pub fn state_for_location<T: BitDenotation>(loc: Location,
+ analysis: &T,
+ result: &DataflowResults<T>)
+ -> IdxSetBuf<T::Idx> {
+ let mut entry = result.sets().on_entry_set_for(loc.block.index()).to_owned();
+
+ {
+ let mut sets = BlockSets {
+ on_entry: &mut entry.clone(),
+ kill_set: &mut entry.clone(),
+ gen_set: &mut entry,
+ };
+
+ for stmt in 0..loc.statement_index {
+ let mut stmt_loc = loc;
+ stmt_loc.statement_index = stmt;
+ analysis.statement_effect(&mut sets, stmt_loc);
+ }
+ }
+
+ entry
+}
+
pub struct DataflowAnalysis<'a, 'tcx: 'a, O> where O: BitDenotation
{
flow_state: DataflowState<O>,
//! This pass computes the meaning of the state field and the MIR locals which are live
//! across a suspension point. There are however two hardcoded generator states:
//! 0 - Generator have not been resumed yet
-//! 1 - Generator has been poisoned
+//! 1 - Generator has returned / is completed
+//! 2 - Generator has been poisoned
//!
//! It also rewrites `return x` and `yield y` as setting a new generator state and returning
//! GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively.
//! the action to take.
//!
//! One of them is the implementation of Generator::resume.
-//! For generators which have already returned it panics.
//! For generators with state 0 (unresumed) it starts the execution of the generator.
-//! For generators with state 1 (poisoned) it panics.
+//! For generators with state 1 (returned) and state 2 (poisoned) it panics.
//! Otherwise it continues the execution from the last suspension point.
//!
//! The other function is the drop glue for the generator.
-//! For generators which have already returned it does nothing.
//! For generators with state 0 (unresumed) it drops the upvars of the generator.
-//! For generators with state 1 (poisoned) it does nothing.
+//! For generators with state 1 (returned) and state 2 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
use rustc::hir;
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::transform::{MirPass, MirSource};
-use rustc::mir::visit::{LvalueContext, MutVisitor};
+use rustc::mir::visit::{LvalueContext, Visitor, MutVisitor};
use rustc::ty::{self, TyCtxt, AdtDef, Ty, GeneratorInterior};
use rustc::ty::subst::{Kind, Substs};
use util::dump_mir;
use util::liveness;
use rustc_const_math::ConstInt;
use rustc_data_structures::indexed_vec::Idx;
+use rustc_data_structures::indexed_set::IdxSetBuf;
use std::collections::HashMap;
use std::borrow::Cow;
use std::iter::once;
use std::mem;
use transform::simplify;
use transform::no_landing_pads::no_landing_pads;
+use dataflow::{self, MaybeStorageLive, state_for_location};
pub struct StateTransform;
Local::new(1)
}
+struct SuspensionPoint {
+ state: u32,
+ resume: BasicBlock,
+ drop: Option<BasicBlock>,
+ storage_liveness: liveness::LocalSet,
+}
+
struct TransformVisitor<'a, 'tcx: 'a> {
tcx: TyCtxt<'a, 'tcx, 'tcx>,
state_adt_ref: &'tcx AdtDef,
// Mapping from Local to (type of local, generator struct index)
remap: HashMap<Local, (Ty<'tcx>, usize)>,
- // The number of generator states. 0 is unresumed, 1 is poisoned. So this is initialized to 2
- bb_target_count: u32,
+ // A map from a suspension point in a block to the locals which have live storage at that point
+ storage_liveness: HashMap<BasicBlock, liveness::LocalSet>,
- // Map from a (which block to resume execution at, which block to use to drop the generator)
- // to a generator state
- bb_targets: HashMap<(BasicBlock, Option<BasicBlock>), u32>,
+ // A list of suspension points, generated during the transform
+ suspension_points: Vec<SuspensionPoint>,
// The original RETURN_POINTER local
new_ret_local: Local,
-
- // The block to resume execution when for Return
- return_block: BasicBlock,
}
impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
let ret_val = match data.terminator().kind {
TerminatorKind::Return => Some((1,
- self.return_block,
+ None,
Operand::Consume(Lvalue::Local(self.new_ret_local)),
None)),
TerminatorKind::Yield { ref value, resume, drop } => Some((0,
- resume,
+ Some(resume),
value.clone(),
drop)),
_ => None
};
if let Some((state_idx, resume, v, drop)) = ret_val {
- let bb_idx = {
- let bb_targets = &mut self.bb_targets;
- let bb_target = &mut self.bb_target_count;
- *bb_targets.entry((resume, drop)).or_insert_with(|| {
- let target = *bb_target;
- *bb_target = target.checked_add(1).unwrap();
- target
- })
- };
let source_info = data.terminator().source_info;
- data.statements.push(self.set_state(bb_idx, source_info));
+ // We must assign the value first in case it gets declared dead below
data.statements.push(Statement {
source_info,
kind: StatementKind::Assign(Lvalue::Local(RETURN_POINTER),
self.make_state(state_idx, v)),
});
+ let state = if let Some(resume) = resume { // Yield
+ let state = 3 + self.suspension_points.len() as u32;
+
+ self.suspension_points.push(SuspensionPoint {
+ state,
+ resume,
+ drop,
+ storage_liveness: self.storage_liveness.get(&block).unwrap().clone(),
+ });
+
+ state
+ } else { // Return
+ 1 // state for returned
+ };
+ data.statements.push(self.set_state(state, source_info));
data.terminator.as_mut().unwrap().kind = TerminatorKind::Return;
}
fn replace_result_variable<'tcx>(ret_ty: Ty<'tcx>,
mir: &mut Mir<'tcx>) -> Local {
- let source_info = SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE,
- };
-
let new_ret = LocalDecl {
mutability: Mutability::Mut,
ty: ret_ty,
name: None,
- source_info,
+ source_info: source_info(mir),
internal: false,
is_user_variable: false,
};
new_ret_local
}
+struct StorageIgnored(liveness::LocalSet);
+
+impl<'tcx> Visitor<'tcx> for StorageIgnored {
+ fn visit_statement(&mut self,
+ _block: BasicBlock,
+ statement: &Statement<'tcx>,
+ _location: Location) {
+ match statement.kind {
+ StatementKind::StorageLive(l) |
+ StatementKind::StorageDead(l) => { self.0.remove(&l); }
+ _ => (),
+ }
+ }
+}
+
fn locals_live_across_suspend_points<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir: &Mir<'tcx>,
- source: MirSource) -> liveness::LocalSet {
+ source: MirSource) ->
+ (liveness::LocalSet,
+ HashMap<BasicBlock, liveness::LocalSet>) {
+ let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
+ let node_id = source.item_id();
+ let analysis = MaybeStorageLive::new(mir);
+ let storage_live =
+ dataflow::do_dataflow(tcx, mir, node_id, &[], &dead_unwinds, analysis,
+ |bd, p| &bd.mir().local_decls[p]);
+
+ let mut ignored = StorageIgnored(IdxSetBuf::new_filled(mir.local_decls.len()));
+ ignored.visit_mir(mir);
+
let mut set = liveness::LocalSet::new_empty(mir.local_decls.len());
- let result = liveness::liveness_of_locals(mir);
- liveness::dump_mir(tcx, "generator_liveness", source, mir, &result);
+ let liveness = liveness::liveness_of_locals(mir);
+ liveness::dump_mir(tcx, "generator_liveness", source, mir, &liveness);
+
+ let mut storage_liveness_map = HashMap::new();
for (block, data) in mir.basic_blocks().iter_enumerated() {
if let TerminatorKind::Yield { .. } = data.terminator().kind {
- set.union(&result.outs[block]);
+ let loc = Location {
+ block: block,
+ statement_index: data.statements.len(),
+ };
+
+ let storage_liveness = state_for_location(loc, &analysis, &storage_live);
+
+ storage_liveness_map.insert(block, storage_liveness.clone());
+
+ let mut live_locals = storage_liveness;
+
+ // Mark locals without storage statements as always having live storage
+ live_locals.union(&ignored.0);
+
+ // Locals live are live at this point only if they are used across suspension points
+ // and their storage is live
+ live_locals.intersect(&liveness.outs[block]);
+
+ // Add the locals life at this suspension point to the set of locals which live across
+ // any suspension points
+ set.union(&live_locals);
}
}
// The generator argument is ignored
set.remove(&self_arg());
- set
+ (set, storage_liveness_map)
}
fn compute_layout<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource,
interior: GeneratorInterior<'tcx>,
mir: &mut Mir<'tcx>)
- -> (HashMap<Local, (Ty<'tcx>, usize)>, GeneratorLayout<'tcx>)
+ -> (HashMap<Local, (Ty<'tcx>, usize)>,
+ GeneratorLayout<'tcx>,
+ HashMap<BasicBlock, liveness::LocalSet>)
{
// Use a liveness analysis to compute locals which are live across a suspension point
- let live_locals = locals_live_across_suspend_points(tcx, mir, source);
+ let (live_locals, storage_liveness) = locals_live_across_suspend_points(tcx, mir, source);
// Erase regions from the types passed in from typeck so we can compare them with
// MIR types
fields: vars
};
- (remap, layout)
+ (remap, layout, storage_liveness)
}
-fn insert_entry_point<'tcx>(mir: &mut Mir<'tcx>,
- block: BasicBlockData<'tcx>) {
- mir.basic_blocks_mut().raw.insert(0, block);
+fn insert_switch<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ mir: &mut Mir<'tcx>,
+ cases: Vec<(u32, BasicBlock)>,
+ transform: &TransformVisitor<'a, 'tcx>) {
+ let return_block = insert_return_block(mir);
+
+ let switch = TerminatorKind::SwitchInt {
+ discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
+ switch_ty: tcx.types.u32,
+ values: Cow::from(cases.iter().map(|&(i, _)| ConstInt::U32(i)).collect::<Vec<_>>()),
+ targets: cases.iter().map(|&(_, d)| d).chain(once(return_block)).collect(),
+ };
+
+ let source_info = source_info(mir);
+ mir.basic_blocks_mut().raw.insert(0, BasicBlockData {
+ statements: Vec::new(),
+ terminator: Some(Terminator {
+ source_info,
+ kind: switch,
+ }),
+ is_cleanup: false,
+ });
let blocks = mir.basic_blocks_mut().iter_mut();
drop_clean: BasicBlock) -> Mir<'tcx> {
let mut mir = mir.clone();
- let source_info = SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE,
- };
+ let source_info = source_info(&mir);
- let return_block = BasicBlock::new(mir.basic_blocks().len());
- mir.basic_blocks_mut().push(BasicBlockData {
- statements: Vec::new(),
- terminator: Some(Terminator {
- source_info,
- kind: TerminatorKind::Return,
- }),
- is_cleanup: false,
- });
-
- let mut cases: Vec<_> = transform.bb_targets.iter().filter_map(|(&(_, u), &s)| {
- u.map(|d| (s, d))
- }).collect();
+ let mut cases = create_cases(&mut mir, transform, |point| point.drop);
cases.insert(0, (0, drop_clean));
- // The poisoned state 1 falls through to the default case which is just to return
+ // The returned state (1) and the poisoned state (2) falls through to
+ // the default case which is just to return
- let switch = TerminatorKind::SwitchInt {
- discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
- switch_ty: tcx.types.u32,
- values: Cow::from(cases.iter().map(|&(i, _)| {
- ConstInt::U32(i)
- }).collect::<Vec<_>>()),
- targets: cases.iter().map(|&(_, d)| d).chain(once(return_block)).collect(),
- };
-
- insert_entry_point(&mut mir, BasicBlockData {
- statements: Vec::new(),
- terminator: Some(Terminator {
- source_info,
- kind: switch,
- }),
- is_cleanup: false,
- });
+ insert_switch(tcx, &mut mir, cases, &transform);
for block in mir.basic_blocks_mut() {
let kind = &mut block.terminator_mut().kind;
}
// Replace the return variable
- let source_info = SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE,
- };
-
mir.return_ty = tcx.mk_nil();
mir.local_decls[RETURN_POINTER] = LocalDecl {
mutability: Mutability::Mut,
mir
}
-fn insert_panic_on_resume_after_return<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
- mir: &mut Mir<'tcx>) {
+fn insert_return_block<'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
+ let return_block = BasicBlock::new(mir.basic_blocks().len());
+ let source_info = source_info(mir);
+ mir.basic_blocks_mut().push(BasicBlockData {
+ statements: Vec::new(),
+ terminator: Some(Terminator {
+ source_info,
+ kind: TerminatorKind::Return,
+ }),
+ is_cleanup: false,
+ });
+ return_block
+}
+
+fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+ mir: &mut Mir<'tcx>,
+ message: AssertMessage<'tcx>) -> BasicBlock {
let assert_block = BasicBlock::new(mir.basic_blocks().len());
let term = TerminatorKind::Assert {
cond: Operand::Constant(box Constant {
},
}),
expected: true,
- msg: AssertMessage::GeneratorResumedAfterReturn,
+ msg: message,
target: assert_block,
cleanup: None,
};
- let source_info = SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE,
- };
-
+ let source_info = source_info(mir);
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
}),
is_cleanup: false,
});
+
+ assert_block
}
fn create_generator_resume_function<'a, 'tcx>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
- mut transform: TransformVisitor<'a, 'tcx>,
+ transform: TransformVisitor<'a, 'tcx>,
def_id: DefId,
source: MirSource,
mir: &mut Mir<'tcx>) {
}
}
- let source_info = SourceInfo {
- span: mir.span,
- scope: ARGUMENT_VISIBILITY_SCOPE,
- };
-
- let poisoned_block = BasicBlock::new(mir.basic_blocks().len());
-
- let term = TerminatorKind::Assert {
- cond: Operand::Constant(box Constant {
- span: mir.span,
- ty: tcx.types.bool,
- literal: Literal::Value {
- value: tcx.mk_const(ty::Const {
- val: ConstVal::Bool(false),
- ty: tcx.types.bool
- }),
- },
- }),
- expected: true,
- msg: AssertMessage::GeneratorResumedAfterPanic,
- target: transform.return_block,
- cleanup: None,
- };
-
- mir.basic_blocks_mut().push(BasicBlockData {
- statements: Vec::new(),
- terminator: Some(Terminator {
- source_info,
- kind: term,
- }),
- is_cleanup: false,
- });
-
- transform.bb_targets.insert((poisoned_block, None), 1);
+ let mut cases = create_cases(mir, &transform, |point| Some(point.resume));
- let switch = TerminatorKind::SwitchInt {
- discr: Operand::Consume(transform.make_field(transform.state_field, tcx.types.u32)),
- switch_ty: tcx.types.u32,
- values: Cow::from(transform.bb_targets.values().map(|&i| {
- ConstInt::U32(i)
- }).collect::<Vec<_>>()),
- targets: transform.bb_targets.keys()
- .map(|&(k, _)| k)
- .chain(once(transform.return_block))
- .collect(),
- };
+ // Jump to the entry point on the 0 state
+ cases.insert(0, (0, BasicBlock::new(0)));
+ // Panic when resumed on the returned (1) state
+ cases.insert(1, (1, insert_panic_block(tcx, mir, AssertMessage::GeneratorResumedAfterReturn)));
+ // Panic when resumed on the poisoned (2) state
+ cases.insert(2, (2, insert_panic_block(tcx, mir, AssertMessage::GeneratorResumedAfterPanic)));
- insert_entry_point(mir, BasicBlockData {
- statements: Vec::new(),
- terminator: Some(Terminator {
- source_info,
- kind: switch,
- }),
- is_cleanup: false,
- });
+ insert_switch(tcx, mir, cases, &transform);
make_generator_state_argument_indirect(tcx, def_id, mir);
dump_mir(tcx, None, "generator_resume", &0, source, mir);
}
-fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
- let source_info = SourceInfo {
+fn source_info<'a, 'tcx>(mir: &Mir<'tcx>) -> SourceInfo {
+ SourceInfo {
span: mir.span,
scope: ARGUMENT_VISIBILITY_SCOPE,
- };
+ }
+}
- let return_block = BasicBlock::new(mir.basic_blocks().len());
- mir.basic_blocks_mut().push(BasicBlockData {
- statements: Vec::new(),
- terminator: Some(Terminator {
- source_info,
- kind: TerminatorKind::Return,
- }),
- is_cleanup: false,
- });
+fn insert_clean_drop<'a, 'tcx>(mir: &mut Mir<'tcx>) -> BasicBlock {
+ let return_block = insert_return_block(mir);
// Create a block to destroy an unresumed generators. This can only destroy upvars.
let drop_clean = BasicBlock::new(mir.basic_blocks().len());
target: return_block,
unwind: None,
};
+ let source_info = source_info(mir);
mir.basic_blocks_mut().push(BasicBlockData {
statements: Vec::new(),
terminator: Some(Terminator {
drop_clean
}
+fn create_cases<'a, 'tcx, F>(mir: &mut Mir<'tcx>,
+ transform: &TransformVisitor<'a, 'tcx>,
+ target: F) -> Vec<(u32, BasicBlock)>
+ where F: Fn(&SuspensionPoint) -> Option<BasicBlock> {
+ let source_info = source_info(mir);
+
+ transform.suspension_points.iter().filter_map(|point| {
+ // Find the target for this suspension point, if applicable
+ target(point).map(|target| {
+ let block = BasicBlock::new(mir.basic_blocks().len());
+ let mut statements = Vec::new();
+
+ // Create StorageLive instructions for locals with live storage
+ for i in 0..(mir.local_decls.len()) {
+ let l = Local::new(i);
+ if point.storage_liveness.contains(&l) && !transform.remap.contains_key(&l) {
+ statements.push(Statement {
+ source_info,
+ kind: StatementKind::StorageLive(l),
+ });
+ }
+ }
+
+ // Then jump to the real target
+ mir.basic_blocks_mut().push(BasicBlockData {
+ statements,
+ terminator: Some(Terminator {
+ source_info,
+ kind: TerminatorKind::Goto {
+ target,
+ },
+ }),
+ is_cleanup: false,
+ });
+
+ (point.state, block)
+ })
+ }).collect()
+}
+
impl MirPass for StateTransform {
fn run_pass<'a, 'tcx>(&self,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
// Extract locals which are live across suspension point into `layout`
// `remap` gives a mapping from local indices onto generator struct indices
- let (remap, layout) = compute_layout(tcx, source, interior, mir);
+ // `storage_liveness` tells us which locals have live storage at suspension points
+ let (remap, layout, storage_liveness) = compute_layout(tcx, source, interior, mir);
let state_field = mir.upvar_decls.len();
- let mut bb_targets = HashMap::new();
-
- // If we jump to the entry point, we should go to the initial 0 generator state.
- // FIXME: Could this result in the need for destruction for state 0?
- bb_targets.insert((BasicBlock::new(0), None), 0);
-
// Run the transformation which converts Lvalues from Local to generator struct
// accesses for locals in `remap`.
// It also rewrites `return x` and `yield y` as writing a new generator state and returning
state_adt_ref,
state_substs,
remap,
- bb_target_count: 2,
- bb_targets,
+ storage_liveness,
+ suspension_points: Vec::new(),
new_ret_local,
state_field,
-
- // For returns we will resume execution at the next added basic block.
- // This happens in `insert_panic_on_resume_after_return`
- return_block: BasicBlock::new(mir.basic_blocks().len()),
};
transform.visit_mir(mir);
mir.spread_arg = None;
mir.generator_layout = Some(layout);
- // Panic if we resumed after returning
- insert_panic_on_resume_after_return(tcx, mir);
-
// Insert `drop(generator_struct)` which is used to drop upvars for generators in
// the unresumed (0) state.
// This is expanded to a drop ladder in `elaborate_generator_drops`.
use back::linker::LinkerInfo;
use back::symbol_export::ExportedSymbols;
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
+use rustc::dep_graph::DepGraph;
use rustc::middle::cstore::{LinkMeta, EncodedMetadata};
use rustc::session::config::{self, OutputFilenames, OutputType, OutputTypes, Passes, SomePasses,
AllPasses, Sanitizer};
}
fn copy_module_artifacts_into_incr_comp_cache(sess: &Session,
+ dep_graph: &DepGraph,
compiled_modules: &CompiledModules,
crate_output: &OutputFilenames) {
if sess.opts.incremental.is_none() {
files.push((OutputType::Bitcode, path));
}
- save_trans_partition(sess, &module.name, module.symbol_name_hash, &files);
+ save_trans_partition(sess,
+ dep_graph,
+ &module.name,
+ module.symbol_name_hash,
+ &files);
}
}
}
impl OngoingCrateTranslation {
- pub fn join(self, sess: &Session) -> CrateTranslation {
+ pub fn join(self, sess: &Session, dep_graph: &DepGraph) -> CrateTranslation {
self.shared_emitter_main.check(sess, true);
let compiled_modules = match self.future.join() {
Ok(compiled_modules) => compiled_modules,
}
copy_module_artifacts_into_incr_comp_cache(sess,
+ dep_graph,
&compiled_modules,
&self.output_filenames);
produce_final_output_artifacts(sess,
use rustc_lint;
use rustc_driver::{driver, target_features, abort_on_err};
use rustc_driver::pretty::ReplaceBodyWithLoop;
-use rustc::dep_graph::DepGraph;
use rustc::session::{self, config};
use rustc::hir::def_id::DefId;
use rustc::hir::def::Def;
false,
Some(codemap.clone()));
- let dep_graph = DepGraph::new(false);
- let _ignore = dep_graph.in_ignore();
let cstore = Rc::new(CStore::new(box rustc_trans::LlvmMetadataLoader));
let mut sess = session::build_session_(
- sessopts, &dep_graph, cpath, diagnostic_handler, codemap
+ sessopts, cpath, diagnostic_handler, codemap,
);
rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
use serialize::json::{ToJson, Json, as_json};
use syntax::{abi, ast};
-use syntax::feature_gate::UnstableFeatures;
use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, DefId};
use rustc::middle::privacy::AccessLevels;
use rustc::middle::stability;
use rustc::hir;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
-use rustc::session::config::nightly_options::is_nightly_build;
use rustc_data_structures::flock;
use clean::{self, AttributesExt, GetDefId, SelfTy, Mutability, Span};
prefix: &str,
scx: &SharedContext)
-> fmt::Result {
- let hoedown_output = format!("{}", Markdown(md_text, RenderType::Hoedown));
// We only emit warnings if the user has opted-in to Pulldown rendering.
let output = if render_type == RenderType::Pulldown {
+ // Save the state of USED_ID_MAP so it only gets updated once even
+ // though we're rendering twice.
+ let orig_used_id_map = USED_ID_MAP.with(|map| map.borrow().clone());
+ let hoedown_output = format!("{}", Markdown(md_text, RenderType::Hoedown));
+ USED_ID_MAP.with(|map| *map.borrow_mut() = orig_used_id_map);
let pulldown_output = format!("{}", Markdown(md_text, RenderType::Pulldown));
let mut differences = html_diff::get_differences(&pulldown_output, &hoedown_output);
differences.retain(|s| {
pulldown_output
} else {
- hoedown_output
+ format!("{}", Markdown(md_text, RenderType::Hoedown))
};
write!(w, "<div class='docblock'>{}{}</div>", prefix, output)
fn item_function(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item,
f: &clean::Function) -> fmt::Result {
- // FIXME(#24111): remove when `const_fn` is stabilized
- let vis_constness = match UnstableFeatures::from_environment() {
- UnstableFeatures::Allow => f.constness,
- _ => hir::Constness::NotConst
- };
let name_len = format!("{}{}{}{:#}fn {}{:#}",
VisSpace(&it.visibility),
- ConstnessSpace(vis_constness),
+ ConstnessSpace(f.constness),
UnsafetySpace(f.unsafety),
AbiSpace(f.abi),
it.name.as_ref().unwrap(),
write!(w, "{vis}{constness}{unsafety}{abi}fn \
{name}{generics}{decl}{where_clause}</pre>",
vis = VisSpace(&it.visibility),
- constness = ConstnessSpace(vis_constness),
+ constness = ConstnessSpace(f.constness),
unsafety = UnsafetySpace(f.unsafety),
abi = AbiSpace(f.abi),
name = it.name.as_ref().unwrap(),
href(did).map(|p| format!("{}#{}.{}", p.0, ty, name)).unwrap_or(anchor)
}
};
- // FIXME(#24111): remove when `const_fn` is stabilized
- let vis_constness = if is_nightly_build() {
- constness
- } else {
- hir::Constness::NotConst
- };
let mut head_len = format!("{}{}{:#}fn {}{:#}",
- ConstnessSpace(vis_constness),
+ ConstnessSpace(constness),
UnsafetySpace(unsafety),
AbiSpace(abi),
name,
};
write!(w, "{}{}{}fn <a href='{href}' class='fnname'>{name}</a>\
{generics}{decl}{where_clause}",
- ConstnessSpace(vis_constness),
+ ConstnessSpace(constness),
UnsafetySpace(unsafety),
AbiSpace(abi),
href = href,
use testing;
use rustc_lint;
-use rustc::dep_graph::DepGraph;
use rustc::hir;
use rustc::hir::intravisit;
use rustc::session::{self, CompileIncomplete, config};
let handler =
errors::Handler::with_tty_emitter(ColorConfig::Auto, true, false, Some(codemap.clone()));
- let dep_graph = DepGraph::new(false);
- let _ignore = dep_graph.in_ignore();
let cstore = Rc::new(CStore::new(box rustc_trans::LlvmMetadataLoader));
let mut sess = session::build_session_(
- sessopts, &dep_graph, Some(input_path.clone()), handler, codemap.clone()
+ sessopts, Some(input_path.clone()), handler, codemap.clone(),
);
rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
let krate = ReplaceBodyWithLoop::new().fold_crate(krate);
let driver::ExpansionResult { defs, mut hir_forest, .. } = {
phase_2_configure_and_expand(
- &sess, &cstore, krate, None, "rustdoc-test", None, MakeGlobMap::No, |_| Ok(())
+ &sess,
+ &cstore,
+ krate,
+ None,
+ "rustdoc-test",
+ None,
+ MakeGlobMap::No,
+ |_| Ok(()),
).expect("phase_2_configure_and_expand aborted in rustdoc!")
};
render_type);
{
- let dep_graph = DepGraph::new(false);
- let _ignore = dep_graph.in_ignore();
let map = hir::map::map_crate(&mut hir_forest, defs);
let krate = map.krate();
let mut hir_collector = HirCollector {
// Compile the code
let diagnostic_handler = errors::Handler::with_emitter(true, false, box emitter);
- let dep_graph = DepGraph::new(false);
let cstore = Rc::new(CStore::new(box rustc_trans::LlvmMetadataLoader));
let mut sess = session::build_session_(
- sessopts, &dep_graph, None, diagnostic_handler, codemap
+ sessopts, None, diagnostic_handler, codemap,
);
rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
opts.maybe_sysroot = Some(sysroot);
let descriptions = Registry::new(&rustc::DIAGNOSTICS);
- let dep_graph = DepGraph::new(opts.build_dep_graph());
let cstore = Rc::new(CStore::new(Box::new(rustc_trans::LlvmMetadataLoader)));
- let sess = build_session(opts, &dep_graph, None, descriptions);
+ let sess = build_session(opts, None, descriptions);
rustc_trans::init(&sess);
rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess));
(sess, cstore)
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(generators)]
+
+enum Enum {
+ A(String),
+ B
+}
+
+fn main() {
+ || {
+ loop {
+ if let true = true {
+ match Enum::A(String::new()) {
+ Enum::A(_var) => {}
+ Enum::B => {}
+ }
+ }
+ yield;
+ }
+ };
+}
\ No newline at end of file
--- /dev/null
+// Copyright 2017 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![feature(const_fn)]
+#![crate_name = "foo"]
+
+// @has foo/fn.bar.html
+// @has - '//*[@class="rust fn"]' 'pub const fn bar() -> '
+/// foo
+pub const fn bar() -> usize {
+ 2
+}
+
+// @has foo/struct.Foo.html
+// @has - '//*[@class="method"]' 'const fn new()'
+pub struct Foo(usize);
+
+impl Foo {
+ pub const fn new() -> Foo { Foo(0) }
+}