diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index 121033549c0d5b0554c208a6551dca5d6e2bf766..f34b14224f7797f0ad798cd4e045e294fdb5104c 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -269,7 +269,7 @@ pub fn as_str(&self) -> String { } /// Setting for how to handle a lint. -#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum Level { Allow, Warn, Deny, Forbid } diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index f1bb3a37e3c273c9ebbfbf39850cff2cd8a3b1f6..dec6f360847bfb2ac6a4928389f6523b266b9a6d 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -73,7 +73,7 @@ pub enum LinkagePreference { } enum_from_u32! { - #[derive(Copy, Clone, PartialEq)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum NativeLibraryKind { NativeStatic, // native static library (.a archive) NativeFramework, // OSX-specific diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 0fb4d0f8fea5e63f171c5ddbc642505775b894f8..e337c1232aab8e475332705c3b0df7a999df2774 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -33,9 +33,15 @@ use errors::{ColorConfig, FatalError, Handler}; use getopts; -use std::collections::HashMap; +use std::collections::{BTreeMap, BTreeSet}; +use std::collections::btree_map::Iter as BTreeMapIter; +use std::collections::btree_map::Keys as BTreeMapKeysIter; +use std::collections::btree_map::Values as BTreeMapValuesIter; + use std::env; use std::fmt; +use std::hash::{Hasher, SipHasher}; +use std::iter::FromIterator; use std::path::PathBuf; pub struct Config { @@ -44,7 +50,7 @@ pub struct Config { pub uint_type: UintTy, } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Hash)] pub enum OptLevel { No, // -O0 Less, // -O1 @@ -54,14 +60,15 @@ pub enum OptLevel { SizeMin, // -Oz } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Hash)] pub enum DebugInfoLevel { NoDebugInfo, LimitedDebugInfo, FullDebugInfo, } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord, + RustcEncodable, RustcDecodable)] pub enum OutputType { Bitcode, Assembly, @@ -118,57 +125,183 @@ pub fn extension(&self) -> &'static str { } } -#[derive(Clone)] -pub struct Options { - // The crate config requested for the session, which may be combined - // with additional crate configurations during the compile process - pub crate_types: Vec, - - pub optimize: OptLevel, - pub debug_assertions: bool, - pub debuginfo: DebugInfoLevel, - pub lint_opts: Vec<(String, lint::Level)>, - pub lint_cap: Option, - pub describe_lints: bool, - pub output_types: HashMap>, - // This was mutable for rustpkg, which updates search paths based on the - // parsed code. It remains mutable in case its replacements wants to use - // this. - pub search_paths: SearchPaths, - pub libs: Vec<(String, cstore::NativeLibraryKind)>, - pub maybe_sysroot: Option, - pub target_triple: String, - // User-specified cfg meta items. The compiler itself will add additional - // items to the crate config, and during parsing the entire crate config - // will be added to the crate AST node. This should not be used for - // anything except building the full crate config prior to parsing. - pub cfg: ast::CrateConfig, - pub test: bool, - pub parse_only: bool, - pub no_trans: bool, - pub error_format: ErrorOutputType, - pub treat_err_as_bug: bool, - pub continue_parse_after_error: bool, - pub mir_opt_level: usize, - - /// if Some, enable incremental compilation, using the given - /// directory to store intermediate results - pub incremental: Option, - - pub no_analysis: bool, - pub debugging_opts: DebuggingOptions, - pub prints: Vec, - pub cg: CodegenOptions, - pub externs: HashMap>, - pub crate_name: Option, - /// An optional name to use as the crate for std during std injection, - /// written `extern crate std = "name"`. Default to "std". Used by - /// out-of-tree drivers. - pub alt_std_name: Option, - /// Indicates how the compiler should treat unstable features - pub unstable_features: UnstableFeatures +// Use tree-based collections to cheaply get a deterministic Hash implementation. +// DO NOT switch BTreeMap out for an unsorted container type! That would break +// dependency tracking for commandline arguments. +#[derive(Clone, Hash)] +pub struct OutputTypes(BTreeMap>); + +impl OutputTypes { + pub fn new(entries: &[(OutputType, Option)]) -> OutputTypes { + OutputTypes(BTreeMap::from_iter(entries.iter() + .map(|&(k, ref v)| (k, v.clone())))) + } + + pub fn get(&self, key: &OutputType) -> Option<&Option> { + self.0.get(key) + } + + pub fn contains_key(&self, key: &OutputType) -> bool { + self.0.contains_key(key) + } + + pub fn keys<'a>(&'a self) -> BTreeMapKeysIter<'a, OutputType, Option> { + self.0.keys() + } + + pub fn values<'a>(&'a self) -> BTreeMapValuesIter<'a, OutputType, Option> { + self.0.values() + } +} + + +// Use tree-based collections to cheaply get a deterministic Hash implementation. +// DO NOT switch BTreeMap or BTreeSet out for an unsorted container type! That +// would break dependency tracking for commandline arguments. +#[derive(Clone, Hash)] +pub struct Externs(BTreeMap>); + +impl Externs { + pub fn new(data: BTreeMap>) -> Externs { + Externs(data) + } + + pub fn get(&self, key: &str) -> Option<&BTreeSet> { + self.0.get(key) + } + + pub fn iter<'a>(&'a self) -> BTreeMapIter<'a, String, BTreeSet> { + self.0.iter() + } +} + +macro_rules! hash_option { + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [UNTRACKED]) => ({}); + ($opt_name:ident, $opt_expr:expr, $sub_hashes:expr, [TRACKED]) => ({ + if $sub_hashes.insert(stringify!($opt_name), + $opt_expr as &dep_tracking::DepTrackingHash).is_some() { + bug!("Duplicate key in CLI DepTrackingHash: {}", stringify!($opt_name)) + } + }); + ($opt_name:ident, + $opt_expr:expr, + $sub_hashes:expr, + [UNTRACKED_WITH_WARNING $warn_val:expr, $warn_text:expr, $error_format:expr]) => ({ + if *$opt_expr == $warn_val { + early_warn($error_format, $warn_text) + } + }); +} + +macro_rules! top_level_options { + (pub struct Options { $( + $opt:ident : $t:ty [$dep_tracking_marker:ident $($warn_val:expr, $warn_text:expr)*], + )* } ) => ( + #[derive(Clone)] + pub struct Options { + $(pub $opt: $t),* + } + + impl Options { + pub fn dep_tracking_hash(&self) -> u64 { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($warn_val, + $warn_text, + self.error_format)*]); + })* + let mut hasher = SipHasher::new(); + dep_tracking::stable_hash(sub_hashes, + &mut hasher, + self.error_format); + hasher.finish() + } + } + ); } +// The top-level commandline options struct +// +// For each option, one has to specify how it behaves with regard to the +// dependency tracking system of incremental compilation. This is done via the +// square-bracketed directive after the field type. The options are: +// +// [TRACKED] +// A change in the given field will cause the compiler to completely clear the +// incremental compilation cache before proceeding. +// +// [UNTRACKED] +// Incremental compilation is not influenced by this option. +// +// [UNTRACKED_WITH_WARNING(val, warning)] +// The option is incompatible with incremental compilation in some way. If it +// has the value `val`, the string `warning` is emitted as a warning. +// +// If you add a new option to this struct or one of the sub-structs like +// CodegenOptions, think about how it influences incremental compilation. If in +// doubt, specify [TRACKED], which is always "correct" but might lead to +// unnecessary re-compilation. +top_level_options!( + pub struct Options { + // The crate config requested for the session, which may be combined + // with additional crate configurations during the compile process + crate_types: Vec [TRACKED], + optimize: OptLevel [TRACKED], + // Include the debug_assertions flag into dependency tracking, since it + // can influence whether overflow checks are done or not. + debug_assertions: bool [TRACKED], + debuginfo: DebugInfoLevel [TRACKED], + lint_opts: Vec<(String, lint::Level)> [TRACKED], + lint_cap: Option [TRACKED], + describe_lints: bool [UNTRACKED], + output_types: OutputTypes [TRACKED], + // FIXME(mw): I'm not entirely sure if this can have any influence on + // incremental compilation apart from what is already handled + // by crate metadata hashes. Better track it. + search_paths: SearchPaths [TRACKED], + // FIXME(mw): Might not need to do dep-tracking for `libs`? + libs: Vec<(String, cstore::NativeLibraryKind)> [TRACKED], + // FIXME(mw): Might not need to do dep-tracking for `maybe_sysroot`? + maybe_sysroot: Option [TRACKED], + + target_triple: String [TRACKED], + + // User-specified cfg meta items. The compiler itself will add additional + // items to the crate config, and during parsing the entire crate config + // will be added to the crate AST node. This should not be used for + // anything except building the full crate config prior to parsing. + // FIXME(mw): If we could be entirely sure that the `cfg` only ever + // influenced which HIR nodes get filtered out, we wouldn't + // need to track this separately. However, we can't rely on + // this (see `debug_assertions` above). + cfg: ast::CrateConfig [TRACKED], + test: bool [TRACKED], + error_format: ErrorOutputType [UNTRACKED], + mir_opt_level: usize [TRACKED], + + // if Some, enable incremental compilation, using the given + // directory to store intermediate results + incremental: Option [UNTRACKED], + + debugging_opts: DebuggingOptions [TRACKED], + prints: Vec [UNTRACKED], + cg: CodegenOptions [TRACKED], + // FIXME(mw): `externs` might not need to be tracked but let's err on + // the side of caution for now. + externs: Externs [TRACKED], + crate_name: Option [TRACKED], + // An optional name to use as the crate for std during std injection, + // written `extern crate std = "name"`. Default to "std". Used by + // out-of-tree drivers. + alt_std_name: Option [TRACKED], + // Indicates how the compiler should treat unstable features + unstable_features: UnstableFeatures [TRACKED], + } +); + #[derive(Clone, PartialEq, Eq)] pub enum PrintRequest { FileNames, @@ -209,7 +342,7 @@ pub struct OutputFilenames { pub out_filestem: String, pub single_output_file: Option, pub extra: String, - pub outputs: HashMap>, + pub outputs: OutputTypes, } /// Codegen unit names generated by the numbered naming scheme will contain this @@ -301,24 +434,19 @@ pub fn basic_options() -> Options { lint_opts: Vec::new(), lint_cap: None, describe_lints: false, - output_types: HashMap::new(), + output_types: OutputTypes(BTreeMap::new()), search_paths: SearchPaths::new(), maybe_sysroot: None, target_triple: host_triple().to_string(), cfg: Vec::new(), test: false, - parse_only: false, - no_trans: false, - treat_err_as_bug: false, - continue_parse_after_error: false, mir_opt_level: 1, incremental: None, - no_analysis: false, debugging_opts: basic_debugging_options(), prints: Vec::new(), cg: basic_codegen_options(), error_format: ErrorOutputType::default(), - externs: HashMap::new(), + externs: Externs(BTreeMap::new()), crate_name: None, alt_std_name: None, libs: Vec::new(), @@ -361,7 +489,7 @@ pub enum CrateType { CrateTypeCdylib, } -#[derive(Clone)] +#[derive(Clone, Hash)] pub enum Passes { SomePasses(Vec), AllPasses, @@ -376,7 +504,7 @@ pub fn is_empty(&self) -> bool { } } -#[derive(Clone, PartialEq)] +#[derive(Clone, PartialEq, Hash)] pub enum PanicStrategy { Unwind, Abort, @@ -405,7 +533,12 @@ pub fn desc(&self) -> &str { ($struct_name:ident, $setter_name:ident, $defaultfn:ident, $buildfn:ident, $prefix:expr, $outputname:expr, $stat:ident, $mod_desc:ident, $mod_set:ident, - $($opt:ident : $t:ty = ($init:expr, $parse:ident, $desc:expr)),* ,) => + $($opt:ident : $t:ty = ( + $init:expr, + $parse:ident, + [$dep_tracking_marker:ident $(($dep_warn_val:expr, $dep_warn_text:expr))*], + $desc:expr) + ),* ,) => ( #[derive(Clone)] pub struct $struct_name { $(pub $opt: $t),* } @@ -457,6 +590,22 @@ pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $s return op; } + impl<'a> dep_tracking::DepTrackingHash for $struct_name { + + fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) { + let mut sub_hashes = BTreeMap::new(); + $({ + hash_option!($opt, + &self.$opt, + &mut sub_hashes, + [$dep_tracking_marker $($dep_warn_val, + $dep_warn_text, + error_format)*]); + })* + dep_tracking::stable_hash(sub_hashes, hasher, error_format); + } + } + pub type $setter_name = fn(&mut $struct_name, v: Option<&str>) -> bool; pub const $stat: &'static [(&'static str, $setter_name, Option<&'static str>, &'static str)] = @@ -622,168 +771,177 @@ fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool { options! {CodegenOptions, CodegenSetter, basic_codegen_options, build_codegen_options, "C", "codegen", CG_OPTIONS, cg_type_desc, cgsetters, - ar: Option = (None, parse_opt_string, + ar: Option = (None, parse_opt_string, [UNTRACKED], "tool to assemble archives with"), - linker: Option = (None, parse_opt_string, + linker: Option = (None, parse_opt_string, [UNTRACKED], "system linker to link outputs with"), - link_args: Option> = (None, parse_opt_list, + link_args: Option> = (None, parse_opt_list, [UNTRACKED], "extra arguments to pass to the linker (space separated)"), - link_dead_code: bool = (false, parse_bool, + link_dead_code: bool = (false, parse_bool, [UNTRACKED], "don't let linker strip dead code (turning it on can be used for code coverage)"), - lto: bool = (false, parse_bool, + lto: bool = (false, parse_bool, [TRACKED], "perform LLVM link-time optimizations"), - target_cpu: Option = (None, parse_opt_string, + target_cpu: Option = (None, parse_opt_string, [TRACKED], "select target processor (rustc --print target-cpus for details)"), - target_feature: String = ("".to_string(), parse_string, + target_feature: String = ("".to_string(), parse_string, [TRACKED], "target specific attributes (rustc --print target-features for details)"), - passes: Vec = (Vec::new(), parse_list, + passes: Vec = (Vec::new(), parse_list, [TRACKED], "a list of extra LLVM passes to run (space separated)"), - llvm_args: Vec = (Vec::new(), parse_list, + llvm_args: Vec = (Vec::new(), parse_list, [TRACKED], "a list of arguments to pass to llvm (space separated)"), - save_temps: bool = (false, parse_bool, + save_temps: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, + "`-C save-temps` might not produce all requested temporary products \ + when incremental compilation is enabled.")], "save all temporary output files during compilation"), - rpath: bool = (false, parse_bool, + rpath: bool = (false, parse_bool, [UNTRACKED], "set rpath values in libs/exes"), - no_prepopulate_passes: bool = (false, parse_bool, + no_prepopulate_passes: bool = (false, parse_bool, [TRACKED], "don't pre-populate the pass manager with a list of passes"), - no_vectorize_loops: bool = (false, parse_bool, + no_vectorize_loops: bool = (false, parse_bool, [TRACKED], "don't run the loop vectorization optimization passes"), - no_vectorize_slp: bool = (false, parse_bool, + no_vectorize_slp: bool = (false, parse_bool, [TRACKED], "don't run LLVM's SLP vectorization pass"), - soft_float: bool = (false, parse_bool, + soft_float: bool = (false, parse_bool, [TRACKED], "generate software floating point library calls"), - prefer_dynamic: bool = (false, parse_bool, + prefer_dynamic: bool = (false, parse_bool, [TRACKED], "prefer dynamic linking to static linking"), - no_integrated_as: bool = (false, parse_bool, + no_integrated_as: bool = (false, parse_bool, [TRACKED], "use an external assembler rather than LLVM's integrated one"), - no_redzone: Option = (None, parse_opt_bool, + no_redzone: Option = (None, parse_opt_bool, [TRACKED], "disable the use of the redzone"), - relocation_model: Option = (None, parse_opt_string, + relocation_model: Option = (None, parse_opt_string, [TRACKED], "choose the relocation model to use (rustc --print relocation-models for details)"), - code_model: Option = (None, parse_opt_string, + code_model: Option = (None, parse_opt_string, [TRACKED], "choose the code model to use (rustc --print code-models for details)"), - metadata: Vec = (Vec::new(), parse_list, + metadata: Vec = (Vec::new(), parse_list, [TRACKED], "metadata to mangle symbol names with"), - extra_filename: String = ("".to_string(), parse_string, + extra_filename: String = ("".to_string(), parse_string, [UNTRACKED], "extra data to put in each output filename"), - codegen_units: usize = (1, parse_uint, + codegen_units: usize = (1, parse_uint, [UNTRACKED], "divide crate into N units to optimize in parallel"), - remark: Passes = (SomePasses(Vec::new()), parse_passes, + remark: Passes = (SomePasses(Vec::new()), parse_passes, [UNTRACKED], "print remarks for these optimization passes (space separated, or \"all\")"), - no_stack_check: bool = (false, parse_bool, + no_stack_check: bool = (false, parse_bool, [UNTRACKED], "disable checks for stack exhaustion (a memory-safety hazard!)"), - debuginfo: Option = (None, parse_opt_uint, + debuginfo: Option = (None, parse_opt_uint, [TRACKED], "debug info emission level, 0 = no debug info, 1 = line tables only, \ 2 = full debug info with variable and type information"), - opt_level: Option = (None, parse_opt_string, + opt_level: Option = (None, parse_opt_string, [TRACKED], "optimize with possible levels 0-3, s, or z"), - debug_assertions: Option = (None, parse_opt_bool, + debug_assertions: Option = (None, parse_opt_bool, [TRACKED], "explicitly enable the cfg(debug_assertions) directive"), - inline_threshold: Option = (None, parse_opt_uint, + inline_threshold: Option = (None, parse_opt_uint, [TRACKED], "set the inlining threshold for"), panic: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy, - "panic strategy to compile crate with"), + [TRACKED], "panic strategy to compile crate with"), } options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, build_debugging_options, "Z", "debugging", DB_OPTIONS, db_type_desc, dbsetters, - verbose: bool = (false, parse_bool, + verbose: bool = (false, parse_bool, [UNTRACKED], "in general, enable more debug printouts"), - time_passes: bool = (false, parse_bool, + time_passes: bool = (false, parse_bool, [UNTRACKED], "measure time of each rustc pass"), count_llvm_insns: bool = (false, parse_bool, + [UNTRACKED_WITH_WARNING(true, + "The output generated by `-Z count_llvm_insns` might not be reliable \ + when used with incremental compilation")], "count where LLVM instrs originate"), - time_llvm_passes: bool = (false, parse_bool, + time_llvm_passes: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, + "The output of `-Z time-llvm-passes` will only reflect timings of \ + re-translated modules when used with incremental compilation" )], "measure time of each LLVM pass"), - input_stats: bool = (false, parse_bool, + input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input"), - trans_stats: bool = (false, parse_bool, + trans_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, + "The output of `-Z trans-stats` might not be accurate when incremental \ + compilation is enabled")], "gather trans statistics"), - asm_comments: bool = (false, parse_bool, + asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior)"), - no_verify: bool = (false, parse_bool, + no_verify: bool = (false, parse_bool, [TRACKED], "skip LLVM verification"), - borrowck_stats: bool = (false, parse_bool, + borrowck_stats: bool = (false, parse_bool, [UNTRACKED], "gather borrowck statistics"), - no_landing_pads: bool = (false, parse_bool, + no_landing_pads: bool = (false, parse_bool, [TRACKED], "omit landing pads for unwinding"), - debug_llvm: bool = (false, parse_bool, + debug_llvm: bool = (false, parse_bool, [UNTRACKED], "enable debug output from LLVM"), - meta_stats: bool = (false, parse_bool, + meta_stats: bool = (false, parse_bool, [UNTRACKED], "gather metadata statistics"), - print_link_args: bool = (false, parse_bool, + print_link_args: bool = (false, parse_bool, [UNTRACKED], "print the arguments passed to the linker"), - print_llvm_passes: bool = (false, parse_bool, + print_llvm_passes: bool = (false, parse_bool, [UNTRACKED], "prints the llvm optimization passes being run"), - ast_json: bool = (false, parse_bool, + ast_json: bool = (false, parse_bool, [UNTRACKED], "print the AST as JSON and halt"), - ast_json_noexpand: bool = (false, parse_bool, + ast_json_noexpand: bool = (false, parse_bool, [UNTRACKED], "print the pre-expansion AST as JSON and halt"), - ls: bool = (false, parse_bool, + ls: bool = (false, parse_bool, [UNTRACKED], "list the symbols defined by a library crate"), - save_analysis: bool = (false, parse_bool, + save_analysis: bool = (false, parse_bool, [UNTRACKED], "write syntax and type analysis (in JSON format) information in addition to normal output"), - save_analysis_csv: bool = (false, parse_bool, + save_analysis_csv: bool = (false, parse_bool, [UNTRACKED], "write syntax and type analysis (in CSV format) information in addition to normal output"), - print_move_fragments: bool = (false, parse_bool, + print_move_fragments: bool = (false, parse_bool, [UNTRACKED], "print out move-fragment data for every fn"), - flowgraph_print_loans: bool = (false, parse_bool, + flowgraph_print_loans: bool = (false, parse_bool, [UNTRACKED], "include loan analysis data in --unpretty flowgraph output"), - flowgraph_print_moves: bool = (false, parse_bool, + flowgraph_print_moves: bool = (false, parse_bool, [UNTRACKED], "include move analysis data in --unpretty flowgraph output"), - flowgraph_print_assigns: bool = (false, parse_bool, + flowgraph_print_assigns: bool = (false, parse_bool, [UNTRACKED], "include assignment analysis data in --unpretty flowgraph output"), - flowgraph_print_all: bool = (false, parse_bool, + flowgraph_print_all: bool = (false, parse_bool, [UNTRACKED], "include all dataflow analysis data in --unpretty flowgraph output"), - print_region_graph: bool = (false, parse_bool, + print_region_graph: bool = (false, parse_bool, [UNTRACKED], "prints region inference graph. \ Use with RUST_REGION_GRAPH=help for more info"), - parse_only: bool = (false, parse_bool, + parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link"), - no_trans: bool = (false, parse_bool, + no_trans: bool = (false, parse_bool, [TRACKED], "run all passes except translation; no output"), - treat_err_as_bug: bool = (false, parse_bool, + treat_err_as_bug: bool = (false, parse_bool, [TRACKED], "treat all errors that occur as bugs"), - continue_parse_after_error: bool = (false, parse_bool, + continue_parse_after_error: bool = (false, parse_bool, [TRACKED], "attempt to recover from parse errors (experimental)"), - incremental: Option = (None, parse_opt_string, + incremental: Option = (None, parse_opt_string, [UNTRACKED], "enable incremental compilation (experimental)"), - incremental_info: bool = (false, parse_bool, + incremental_info: bool = (false, parse_bool, [UNTRACKED], "print high-level information about incremental reuse (or the lack thereof)"), - dump_dep_graph: bool = (false, parse_bool, + dump_dep_graph: bool = (false, parse_bool, [UNTRACKED], "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"), - query_dep_graph: bool = (false, parse_bool, + query_dep_graph: bool = (false, parse_bool, [UNTRACKED], "enable queries of the dependency graph for regression testing"), - no_analysis: bool = (false, parse_bool, + no_analysis: bool = (false, parse_bool, [UNTRACKED], "parse and expand the source, but run no analysis"), - extra_plugins: Vec = (Vec::new(), parse_list, + extra_plugins: Vec = (Vec::new(), parse_list, [TRACKED], "load extra plugins"), - unstable_options: bool = (false, parse_bool, + unstable_options: bool = (false, parse_bool, [UNTRACKED], "adds unstable command line options to rustc interface"), - force_overflow_checks: Option = (None, parse_opt_bool, + force_overflow_checks: Option = (None, parse_opt_bool, [TRACKED], "force overflow checks on or off"), - force_dropflag_checks: Option = (None, parse_opt_bool, + force_dropflag_checks: Option = (None, parse_opt_bool, [TRACKED], "force drop flag checks on or off"), - trace_macros: bool = (false, parse_bool, + trace_macros: bool = (false, parse_bool, [UNTRACKED], "for every macro invocation, print its name and arguments"), - enable_nonzeroing_move_hints: bool = (false, parse_bool, + enable_nonzeroing_move_hints: bool = (false, parse_bool, [TRACKED], "force nonzeroing move optimization on"), - keep_hygiene_data: bool = (false, parse_bool, + keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED], "don't clear the hygiene data after analysis"), - keep_ast: bool = (false, parse_bool, + keep_ast: bool = (false, parse_bool, [UNTRACKED], "keep the AST after lowering it to HIR"), - show_span: Option = (None, parse_opt_string, + show_span: Option = (None, parse_opt_string, [TRACKED], "show spans for compiler debugging (expr|pat|ty)"), - print_trans_items: Option = (None, parse_opt_string, + print_trans_items: Option = (None, parse_opt_string, [UNTRACKED], "print the result of the translation item collection pass"), - mir_opt_level: Option = (None, parse_opt_uint, + mir_opt_level: Option = (None, parse_opt_uint, [TRACKED], "set the MIR optimization level (0-3)"), - dump_mir: Option = (None, parse_opt_string, + dump_mir: Option = (None, parse_opt_string, [UNTRACKED], "dump MIR state at various points in translation"), - dump_mir_dir: Option = (None, parse_opt_string, + dump_mir_dir: Option = (None, parse_opt_string, [UNTRACKED], "the directory the MIR is dumped into"), - orbit: bool = (true, parse_all_bool, + orbit: bool = (true, parse_all_bool, [UNTRACKED], "get MIR where it belongs - everywhere; most importantly, in orbit"), } @@ -1185,14 +1343,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { debugging_opts.orbit = true; } - let parse_only = debugging_opts.parse_only; - let no_trans = debugging_opts.no_trans; - let treat_err_as_bug = debugging_opts.treat_err_as_bug; - let continue_parse_after_error = debugging_opts.continue_parse_after_error; let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1); - let no_analysis = debugging_opts.no_analysis; - let mut output_types = HashMap::new(); + let mut output_types = BTreeMap::new(); if !debugging_opts.parse_only { for list in matches.opt_strs("emit") { for output_type in list.split(',') { @@ -1360,7 +1513,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { --debuginfo"); } - let mut externs = HashMap::new(); + let mut externs = BTreeMap::new(); for arg in &matches.opt_strs("extern") { let mut parts = arg.splitn(2, '='); let name = match parts.next() { @@ -1372,7 +1525,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { None => early_error(error_format, "--extern value must be of the format `foo=bar`"), }; - externs.entry(name.to_string()).or_insert(vec![]).push(location.to_string()); + externs.entry(name.to_string()) + .or_insert_with(BTreeSet::new) + .insert(location.to_string()); } let crate_name = matches.opt_str("crate-name"); @@ -1386,24 +1541,19 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { lint_opts: lint_opts, lint_cap: lint_cap, describe_lints: describe_lints, - output_types: output_types, + output_types: OutputTypes(output_types), search_paths: search_paths, maybe_sysroot: sysroot_opt, target_triple: target, cfg: cfg, test: test, - parse_only: parse_only, - no_trans: no_trans, - treat_err_as_bug: treat_err_as_bug, - continue_parse_after_error: continue_parse_after_error, mir_opt_level: mir_opt_level, incremental: incremental, - no_analysis: no_analysis, debugging_opts: debugging_opts, prints: prints, cg: cg, error_format: error_format, - externs: externs, + externs: Externs(externs), crate_name: crate_name, alt_std_name: None, libs: libs, @@ -1530,17 +1680,205 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { } } +/// Commandline arguments passed to the compiler have to be incorporated with +/// the dependency tracking system for incremental compilation. This module +/// provides some utilities to make this more convenient. +/// +/// The values of all commandline arguments that are relevant for dependency +/// tracking are hashed into a single value that determines whether the +/// incremental compilation cache can be re-used or not. This hashing is done +/// via the DepTrackingHash trait defined below, since the standard Hash +/// implementation might not be suitable (e.g. arguments are stored in a Vec, +/// the hash of which is order dependent, but we might not want the order of +/// arguments to make a difference for the hash). +/// +/// However, since the value provided by Hash::hash often *is* suitable, +/// especially for primitive types, there is the +/// impl_dep_tracking_hash_via_hash!() macro that allows to simply reuse the +/// Hash implementation for DepTrackingHash. It's important though that +/// we have an opt-in scheme here, so one is hopefully forced to think about +/// how the hash should be calculated when adding a new commandline argument. +mod dep_tracking { + use lint; + use middle::cstore; + use session::search_paths::{PathKind, SearchPaths}; + use std::collections::BTreeMap; + use std::hash::{Hash, SipHasher}; + use std::path::PathBuf; + use super::{Passes, PanicStrategy, CrateType, OptLevel, DebugInfoLevel, + OutputTypes, Externs, ErrorOutputType}; + use syntax::ast; + use syntax::feature_gate::UnstableFeatures; + use syntax::parse::token::InternedString; + use syntax::ptr::P; + + pub trait DepTrackingHash { + fn hash(&self, &mut SipHasher, ErrorOutputType); + } + + macro_rules! impl_dep_tracking_hash_via_hash { + ($t:ty) => ( + impl DepTrackingHash for $t { + fn hash(&self, hasher: &mut SipHasher, _: ErrorOutputType) { + Hash::hash(self, hasher); + } + } + ) + } + + macro_rules! impl_dep_tracking_hash_for_sortable_vec_of { + ($t:ty) => ( + impl DepTrackingHash for Vec<$t> { + fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) { + let mut elems = self.clone(); + elems.sort(); + for (i, e) in elems.iter().enumerate() { + Hash::hash(&i, hasher); + DepTrackingHash::hash(e, hasher, error_format); + } + } + } + ); + } + + impl_dep_tracking_hash_via_hash!(bool); + impl_dep_tracking_hash_via_hash!(usize); + impl_dep_tracking_hash_via_hash!(String); + impl_dep_tracking_hash_via_hash!(lint::Level); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(Option); + impl_dep_tracking_hash_via_hash!(CrateType); + impl_dep_tracking_hash_via_hash!(PanicStrategy); + impl_dep_tracking_hash_via_hash!(Passes); + impl_dep_tracking_hash_via_hash!(OptLevel); + impl_dep_tracking_hash_via_hash!(DebugInfoLevel); + impl_dep_tracking_hash_via_hash!(UnstableFeatures); + impl_dep_tracking_hash_via_hash!(Externs); + impl_dep_tracking_hash_via_hash!(OutputTypes); + impl_dep_tracking_hash_via_hash!(cstore::NativeLibraryKind); + + impl_dep_tracking_hash_for_sortable_vec_of!(String); + impl_dep_tracking_hash_for_sortable_vec_of!(CrateType); + impl_dep_tracking_hash_for_sortable_vec_of!((String, lint::Level)); + impl_dep_tracking_hash_for_sortable_vec_of!((String, cstore::NativeLibraryKind)); + + impl DepTrackingHash for SearchPaths { + fn hash(&self, hasher: &mut SipHasher, _: ErrorOutputType) { + let mut elems: Vec<_> = self + .iter(PathKind::All) + .collect(); + elems.sort(); + Hash::hash(&elems, hasher); + } + } + + fn sorted_meta_items(items: &[P]) -> Vec<&ast::MetaItem> { + // Sort subitems so the hash does not depend on their order + let mut items: Vec<&ast::MetaItem> = items.iter() + .map(|r| &**r) + .collect(); + items.sort_by_key(meta_item_sort_key); + return items; + + fn meta_item_sort_key(item: &&ast::MetaItem) -> InternedString { + match item.node { + ast::MetaItemKind::Word(ref s) | + ast::MetaItemKind::NameValue(ref s, _) | + ast::MetaItemKind::List(ref s, _) => s.clone() + } + } + } + + impl DepTrackingHash for ast::MetaItem { + fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) { + // ignoring span information, it doesn't matter here + match self.node { + ast::MetaItemKind::Word(ref s) => { + Hash::hash("Word", hasher); + Hash::hash(&s.len(), hasher); + Hash::hash(s, hasher); + } + ast::MetaItemKind::NameValue(ref s, ref lit) => { + Hash::hash("NameValue", hasher); + Hash::hash(&s.len(), hasher); + Hash::hash(s, hasher); + Hash::hash(&lit.node, hasher); + } + ast::MetaItemKind::List(ref s, ref items) => { + Hash::hash("List", hasher); + Hash::hash(&s.len(), hasher); + Hash::hash(s, hasher); + // Sort subitems so the hash does not depend on their order + let sorted = sorted_meta_items(&items[..]); + for (index, item) in sorted.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(*item, hasher, error_format); + } + } + } + } + } + + impl DepTrackingHash for ast::CrateConfig { + fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) { + // Sort subitems so the hash does not depend on their order + let sorted = sorted_meta_items(&self[..]); + for (index, item) in sorted.iter().enumerate() { + Hash::hash(&index, hasher); + DepTrackingHash::hash(*item, hasher, error_format); + } + } + } + + impl DepTrackingHash for (T1, T2) + where T1: DepTrackingHash, + T2: DepTrackingHash + { + fn hash(&self, hasher: &mut SipHasher, error_format: ErrorOutputType) { + Hash::hash(&0, hasher); + DepTrackingHash::hash(&self.0, hasher, error_format); + Hash::hash(&1, hasher); + DepTrackingHash::hash(&self.1, hasher, error_format); + } + } + + // This is a stable hash because BTreeMap is a sorted container + pub fn stable_hash(sub_hashes: BTreeMap<&'static str, &DepTrackingHash>, + hasher: &mut SipHasher, + error_format: ErrorOutputType) { + for (key, sub_hash) in sub_hashes { + // Using Hash::hash() instead of DepTrackingHash::hash() is fine for + // the keys, as they are just plain strings + Hash::hash(&key.len(), hasher); + Hash::hash(key, hasher); + sub_hash.hash(hasher, error_format); + } + } +} + #[cfg(test)] mod tests { use dep_graph::DepGraph; - use middle::cstore::DummyCrateStore; + use errors; + use getopts::{getopts, OptGroup}; + use lint; + use middle::cstore::{self, DummyCrateStore}; use session::config::{build_configuration, build_session_options}; use session::build_session; - use errors; + use std::collections::{BTreeMap, BTreeSet}; + use std::iter::FromIterator; + use std::path::PathBuf; use std::rc::Rc; - use getopts::{getopts, OptGroup}; + use super::{OutputType, OutputTypes, Externs, PanicStrategy}; + use syntax::ast::{self, MetaItemKind}; use syntax::attr; use syntax::attr::AttrMetaMethods; + use syntax::codemap::dummy_spanned; + use syntax::parse::token::InternedString; + use syntax::ptr::P; fn optgroups() -> Vec { super::rustc_optgroups().into_iter() @@ -1548,6 +1886,14 @@ fn optgroups() -> Vec { .collect() } + fn mk_map(entries: Vec<(K, V)>) -> BTreeMap { + BTreeMap::from_iter(entries.into_iter()) + } + + fn mk_set(entries: Vec) -> BTreeSet { + BTreeSet::from_iter(entries.into_iter()) + } + // When the user supplies --test we should implicitly supply --cfg test #[test] fn test_switch_implies_cfg_test() { @@ -1624,4 +1970,704 @@ fn test_can_print_warnings() { assert!(sess.diagnostic().can_emit_warnings); } } + + #[test] + fn test_output_types_tracking_hash_different_paths() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + v1.output_types = OutputTypes::new(&[(OutputType::Exe, + Some(PathBuf::from("./some/thing")))]); + v2.output_types = OutputTypes::new(&[(OutputType::Exe, + Some(PathBuf::from("/some/thing")))]); + v3.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_output_types_tracking_hash_different_construction_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + + v1.output_types = OutputTypes::new(&[ + (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + ]); + + v2.output_types = OutputTypes::new(&[ + (OutputType::Bitcode, Some(PathBuf::from("./some/thing.bc"))), + (OutputType::Exe, Some(PathBuf::from("./some/thing"))), + ]); + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + } + + #[test] + fn test_externs_tracking_hash_different_values() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + v1.externs = Externs::new(mk_map(vec![ + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + (String::from("d"), mk_set(vec![String::from("e"), + String::from("f")])), + ])); + + v2.externs = Externs::new(mk_map(vec![ + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + (String::from("X"), mk_set(vec![String::from("e"), + String::from("f")])), + ])); + + v3.externs = Externs::new(mk_map(vec![ + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + (String::from("d"), mk_set(vec![String::from("X"), + String::from("f")])), + ])); + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_externs_tracking_hash_different_construction_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + v1.externs = Externs::new(mk_map(vec![ + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + (String::from("d"), mk_set(vec![String::from("e"), + String::from("f")])), + ])); + + v2.externs = Externs::new(mk_map(vec![ + (String::from("d"), mk_set(vec![String::from("e"), + String::from("f")])), + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + ])); + + v3.externs = Externs::new(mk_map(vec![ + (String::from("a"), mk_set(vec![String::from("b"), + String::from("c")])), + (String::from("d"), mk_set(vec![String::from("f"), + String::from("e")])), + ])); + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + assert_eq!(v1.dep_tracking_hash(), v3.dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_lints_tracking_hash_different_values() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + v1.lint_opts = vec![(String::from("a"), lint::Allow), + (String::from("b"), lint::Warn), + (String::from("c"), lint::Deny), + (String::from("d"), lint::Forbid)]; + + v2.lint_opts = vec![(String::from("a"), lint::Allow), + (String::from("b"), lint::Warn), + (String::from("X"), lint::Deny), + (String::from("d"), lint::Forbid)]; + + v3.lint_opts = vec![(String::from("a"), lint::Allow), + (String::from("b"), lint::Warn), + (String::from("c"), lint::Forbid), + (String::from("d"), lint::Deny)]; + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_lints_tracking_hash_different_construction_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + + v1.lint_opts = vec![(String::from("a"), lint::Allow), + (String::from("b"), lint::Warn), + (String::from("c"), lint::Deny), + (String::from("d"), lint::Forbid)]; + + v2.lint_opts = vec![(String::from("a"), lint::Allow), + (String::from("c"), lint::Deny), + (String::from("b"), lint::Warn), + (String::from("d"), lint::Forbid)]; + + assert_eq!(v1.dep_tracking_hash(), v2.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + } + + #[test] + fn test_crate_config_tracking_hash_different_values() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + let mut v4 = super::basic_options(); + + // Reference value + v1.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("b"), + vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(1))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + // Change a label + v2.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("X"), + vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(1))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + // Change a literal + v3.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("X"), + vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(99))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + // Remove something + v4.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("X"), + vec![ + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(99))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); + } + + #[test] + fn test_crate_config_tracking_hash_different_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + // Reference value + v1.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("b"), + vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(1))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + v2.cfg = vec![ + P(dummy_spanned(MetaItemKind::List(InternedString::new("b"), + vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(1))))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + ]; + + v3.cfg = vec![ + P(dummy_spanned(MetaItemKind::Word(InternedString::new("a")))), + P(dummy_spanned(MetaItemKind::List(InternedString::new("b"), + vec![ + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("d"), + dummy_spanned(ast::LitKind::Byte(1))))), + P(dummy_spanned(MetaItemKind::Word(InternedString::new("c")))), + ]))), + P(dummy_spanned(MetaItemKind::NameValue(InternedString::new("e"), + dummy_spanned(ast::LitKind::Byte(2))))), + ]; + + assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_search_paths_tracking_hash_different_values() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + let mut v4 = super::basic_options(); + let mut v5 = super::basic_options(); + + // Reference + v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + // Native changed + v2.search_paths.add_path("native=XXX", super::ErrorOutputType::Json); + v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + // Crate changed + v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v2.search_paths.add_path("crate=XXX", super::ErrorOutputType::Json); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + // Dependency changed + v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v3.search_paths.add_path("dependency=XXX", super::ErrorOutputType::Json); + v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + // Framework changed + v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v4.search_paths.add_path("framework=XXX", super::ErrorOutputType::Json); + v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + // All changed + v5.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v5.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v5.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v5.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v5.search_paths.add_path("all=XXX", super::ErrorOutputType::Json); + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v4.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v5.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); + assert_eq!(v5.dep_tracking_hash(), v5.clone().dep_tracking_hash()); + } + + #[test] + fn test_search_paths_tracking_hash_different_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + let mut v4 = super::basic_options(); + + // Reference + v1.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v1.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v1.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v1.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v1.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + v2.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v2.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v2.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v2.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v2.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + v3.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v3.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + v3.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v3.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v3.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + + v4.search_paths.add_path("all=mno", super::ErrorOutputType::Json); + v4.search_paths.add_path("native=abc", super::ErrorOutputType::Json); + v4.search_paths.add_path("crate=def", super::ErrorOutputType::Json); + v4.search_paths.add_path("dependency=ghi", super::ErrorOutputType::Json); + v4.search_paths.add_path("framework=jkl", super::ErrorOutputType::Json); + + assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v4.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + assert_eq!(v4.dep_tracking_hash(), v4.clone().dep_tracking_hash()); + } + + #[test] + fn test_native_libs_tracking_hash_different_values() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + // Reference + v1.libs = vec![(String::from("a"), cstore::NativeStatic), + (String::from("b"), cstore::NativeFramework), + (String::from("c"), cstore::NativeUnknown)]; + + // Change label + v2.libs = vec![(String::from("a"), cstore::NativeStatic), + (String::from("X"), cstore::NativeFramework), + (String::from("c"), cstore::NativeUnknown)]; + + // Change kind + v3.libs = vec![(String::from("a"), cstore::NativeStatic), + (String::from("b"), cstore::NativeStatic), + (String::from("c"), cstore::NativeUnknown)]; + + assert!(v1.dep_tracking_hash() != v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() != v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_native_libs_tracking_hash_different_order() { + let mut v1 = super::basic_options(); + let mut v2 = super::basic_options(); + let mut v3 = super::basic_options(); + + // Reference + v1.libs = vec![(String::from("a"), cstore::NativeStatic), + (String::from("b"), cstore::NativeFramework), + (String::from("c"), cstore::NativeUnknown)]; + + v2.libs = vec![(String::from("b"), cstore::NativeFramework), + (String::from("a"), cstore::NativeStatic), + (String::from("c"), cstore::NativeUnknown)]; + + v3.libs = vec![(String::from("c"), cstore::NativeUnknown), + (String::from("a"), cstore::NativeStatic), + (String::from("b"), cstore::NativeFramework)]; + + assert!(v1.dep_tracking_hash() == v2.dep_tracking_hash()); + assert!(v1.dep_tracking_hash() == v3.dep_tracking_hash()); + assert!(v2.dep_tracking_hash() == v3.dep_tracking_hash()); + + // Check clone + assert_eq!(v1.dep_tracking_hash(), v1.clone().dep_tracking_hash()); + assert_eq!(v2.dep_tracking_hash(), v2.clone().dep_tracking_hash()); + assert_eq!(v3.dep_tracking_hash(), v3.clone().dep_tracking_hash()); + } + + #[test] + fn test_codegen_options_tracking_hash() { + let reference = super::basic_options(); + let mut opts = super::basic_options(); + + // Make sure the changing an [UNTRACKED] option leaves the hash unchanged + opts.cg.ar = Some(String::from("abc")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.linker = Some(String::from("linker")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.link_args = Some(vec![String::from("abc"), String::from("def")]); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.link_dead_code = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.rpath = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.extra_filename = String::from("extra-filename"); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.codegen_units = 42; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.remark = super::SomePasses(vec![String::from("pass1"), + String::from("pass2")]); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + opts.cg.save_temps = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + + // Make sure changing a [TRACKED] option changes the hash + opts = reference.clone(); + opts.cg.lto = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.target_cpu = Some(String::from("abc")); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.target_feature = String::from("all the features, all of them"); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.passes = vec![String::from("1"), String::from("2")]; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.llvm_args = vec![String::from("1"), String::from("2")]; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.no_prepopulate_passes = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.no_vectorize_loops = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.no_vectorize_slp = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.soft_float = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.prefer_dynamic = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.no_integrated_as = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.no_redzone = Some(true); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.relocation_model = Some(String::from("relocation model")); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.code_model = Some(String::from("code model")); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.metadata = vec![String::from("A"), String::from("B")]; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.debuginfo = Some(0xdeadbeef); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.debuginfo = Some(0xba5eba11); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.debug_assertions = Some(true); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.inline_threshold = Some(0xf007ba11); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.cg.panic = PanicStrategy::Abort; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + } + + #[test] + fn test_debugging_options_tracking_hash() { + let reference = super::basic_options(); + let mut opts = super::basic_options(); + + // Make sure the changing an [UNTRACKED] option leaves the hash unchanged + opts.debugging_opts.verbose = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.time_passes = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.count_llvm_insns = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.time_llvm_passes = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.input_stats = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.trans_stats = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.borrowck_stats = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.debug_llvm = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.meta_stats = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.print_link_args = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.print_llvm_passes = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.ast_json = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.ast_json_noexpand = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.ls = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.save_analysis = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.save_analysis_csv = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.print_move_fragments = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.flowgraph_print_loans = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.flowgraph_print_moves = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.flowgraph_print_assigns = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.flowgraph_print_all = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.print_region_graph = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.parse_only = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.incremental = Some(String::from("abc")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.dump_dep_graph = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.query_dep_graph = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.no_analysis = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.unstable_options = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.trace_macros = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.keep_hygiene_data = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.keep_ast = true; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.print_trans_items = Some(String::from("abc")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.dump_mir = Some(String::from("abc")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.dump_mir_dir = Some(String::from("abc")); + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + opts.debugging_opts.orbit = false; + assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); + + // Make sure changing a [TRACKED] option changes the hash + opts = reference.clone(); + opts.debugging_opts.asm_comments = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.no_verify = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.no_landing_pads = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.no_trans = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.treat_err_as_bug = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.continue_parse_after_error = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.extra_plugins = vec![String::from("plugin1"), String::from("plugin2")]; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.force_overflow_checks = Some(true); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.force_dropflag_checks = Some(true); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.enable_nonzeroing_move_hints = true; + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.show_span = Some(String::from("abc")); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + + opts = reference.clone(); + opts.debugging_opts.mir_opt_level = Some(1); + assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); + } } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 9ab75c8a5a20c8e17a9c47c3d5c70c18d779ccc0..c71253aee568f9a1e0cd8dcc905557cd7fa7bb20 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -363,7 +363,7 @@ pub fn build_session_with_codemap(sopts: config::Options, .map(|&(_, ref level)| *level != lint::Allow) .last() .unwrap_or(true); - let treat_err_as_bug = sopts.treat_err_as_bug; + let treat_err_as_bug = sopts.debugging_opts.treat_err_as_bug; let emitter: Box = match sopts.error_format { config::ErrorOutputType::HumanReadable(color_config) => { diff --git a/src/librustc/session/search_paths.rs b/src/librustc/session/search_paths.rs index 3c6cd26bef6ce87bfc853f040abae95d848a14a2..5bbc6841693ea3628c75c8ebf35988b046485ac1 100644 --- a/src/librustc/session/search_paths.rs +++ b/src/librustc/session/search_paths.rs @@ -22,7 +22,7 @@ pub struct Iter<'a> { iter: slice::Iter<'a, (PathKind, PathBuf)>, } -#[derive(Eq, PartialEq, Clone, Copy, Debug)] +#[derive(Eq, PartialEq, Clone, Copy, Debug, PartialOrd, Ord, Hash)] pub enum PathKind { Native, Crate, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 7f50522b2032b0e10f51d9cdb38f20efe7a9a182..aa794829be2caa556ca8a9dd2a1a3cc1092b8155 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -15,7 +15,8 @@ use rustc_mir as mir; use rustc::mir::mir_map::MirMap; use rustc::session::{Session, CompileResult, compile_result_from_err_count}; -use rustc::session::config::{self, Input, OutputFilenames, OutputType}; +use rustc::session::config::{self, Input, OutputFilenames, OutputType, + OutputTypes}; use rustc::session::search_paths::PathKind; use rustc::lint; use rustc::middle::{self, dependency_format, stability, reachable}; @@ -42,7 +43,6 @@ use serialize::json; -use std::collections::HashMap; use std::env; use std::ffi::{OsString, OsStr}; use std::fs; @@ -478,7 +478,7 @@ pub fn phase_1_parse_input<'a>(sess: &'a Session, cfg: ast::CrateConfig, input: &Input) -> PResult<'a, ast::Crate> { - let continue_after_error = sess.opts.continue_parse_after_error; + let continue_after_error = sess.opts.debugging_opts.continue_parse_after_error; sess.diagnostic().set_continue_after_error(continue_after_error); let krate = time(sess.time_passes(), "parsing", || { @@ -1026,11 +1026,10 @@ pub fn phase_5_run_llvm_passes(sess: &Session, trans: &trans::CrateTranslation, outputs: &OutputFilenames) -> CompileResult { if sess.opts.cg.no_integrated_as { - let mut map = HashMap::new(); - map.insert(OutputType::Assembly, None); + let output_types = OutputTypes::new(&[(OutputType::Assembly, None)]); time(sess.time_passes(), "LLVM passes", - || write::run_passes(sess, trans, &map, outputs)); + || write::run_passes(sess, trans, &output_types, outputs)); write::run_assembler(sess, outputs); diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 98860c8f900eb397a68851509144182c89bc8364..b2a6df8d345c9dd4750e9cbd864dd17e93b6b7d8 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -506,12 +506,14 @@ fn build_controller(&mut self, return control; } - if sess.opts.parse_only || sess.opts.debugging_opts.show_span.is_some() || + if sess.opts.debugging_opts.parse_only || + sess.opts.debugging_opts.show_span.is_some() || sess.opts.debugging_opts.ast_json_noexpand { control.after_parse.stop = Compilation::Stop; } - if sess.opts.no_analysis || sess.opts.debugging_opts.ast_json { + if sess.opts.debugging_opts.no_analysis || + sess.opts.debugging_opts.ast_json { control.after_hir_lowering.stop = Compilation::Stop; } diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs index 48c8bcff1ec560a12db7bdba3f24b9be15b922ba..543a9c88a586a847b8bac39337ed63848b0f870d 100644 --- a/src/librustc_metadata/loader.rs +++ b/src/librustc_metadata/loader.rs @@ -400,7 +400,7 @@ fn find_library_crate(&mut self) -> Option { if self.hash.is_none() { self.should_match_name = false; if let Some(s) = self.sess.opts.externs.get(self.crate_name) { - return self.find_commandline_library(s); + return self.find_commandline_library(s.iter()); } self.should_match_name = true; } @@ -661,7 +661,9 @@ fn staticlibname(&self) -> (String, String) { (t.options.staticlib_prefix.clone(), t.options.staticlib_suffix.clone()) } - fn find_commandline_library(&mut self, locs: &[String]) -> Option { + fn find_commandline_library<'b, LOCS> (&mut self, locs: LOCS) -> Option + where LOCS: Iterator + { // First, filter out all libraries that look suspicious. We only accept // files which actually exist that have the correct naming scheme for // rlibs/dylibs. @@ -670,7 +672,7 @@ fn find_commandline_library(&mut self, locs: &[String]) -> Option { let mut rlibs = HashMap::new(); let mut dylibs = HashMap::new(); { - let locs = locs.iter().map(|l| PathBuf::from(l)).filter(|loc| { + let locs = locs.map(|l| PathBuf::from(l)).filter(|loc| { if !loc.exists() { sess.err(&format!("extern location for {} does not exist: {}", self.crate_name, loc.display())); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index a9f3d2f8a175485b954c016e6d2e207a7c4084ff..f17d1a7f1cf31bd04aae8fe593632bef4f97edc0 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -190,7 +190,8 @@ pub fn link_binary(sess: &Session, let mut out_filenames = Vec::new(); for &crate_type in sess.crate_types.borrow().iter() { // Ignore executable crates if we have -Z no-trans, as they will error. - if sess.opts.no_trans && crate_type == config::CrateTypeExecutable { + if sess.opts.debugging_opts.no_trans && + crate_type == config::CrateTypeExecutable { continue; } diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index b5c993b86ecb2e0c09d015f9d93117ec02534b60..4b9c29d3d7db3a57b4f17063464315143175d6fb 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -11,7 +11,7 @@ use back::lto; use back::link::{get_linker, remove}; use rustc_incremental::save_trans_partition; -use session::config::{OutputFilenames, Passes, SomePasses, AllPasses}; +use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses}; use session::Session; use session::config::{self, OutputType}; use llvm; @@ -26,7 +26,6 @@ use syntax_pos::MultiSpan; use context::{is_pie_binary, get_reloc_model}; -use std::collections::HashMap; use std::ffi::{CStr, CString}; use std::fs; use std::path::{Path, PathBuf}; @@ -641,7 +640,7 @@ pub fn cleanup_llvm(trans: &CrateTranslation) { pub fn run_passes(sess: &Session, trans: &CrateTranslation, - output_types: &HashMap>, + output_types: &OutputTypes, crate_output: &OutputFilenames) { // It's possible that we have `codegen_units > 1` but only one item in // `trans.modules`. We could theoretically proceed and do LTO in that diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1077cb296c1ac307e2e5a07535ed1eaf11166825..a79eca15a369a2cb81a8e93bb31247e573c0abf3 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -2569,7 +2569,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, assert_module_sources::assert_module_sources(tcx, &modules); // Skip crate items and just output metadata in -Z no-trans mode. - if tcx.sess.opts.no_trans { + if tcx.sess.opts.debugging_opts.no_trans { let linker_info = LinkerInfo::new(&shared_ccx, &[]); return CrateTranslation { modules: modules, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7d8ebc403b08f1c5cc59f050a0df042ce42a5650..863b787c58d7401384e7d31c31b64b04c8f77454 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -45,7 +45,6 @@ pub enum MaybeTyped<'a, 'tcx: 'a> { NotTyped(&'a session::Session) } -pub type Externs = HashMap>; pub type ExternalPaths = HashMap, clean::TypeKind)>; pub struct DocContext<'a, 'tcx: 'a> { @@ -99,7 +98,7 @@ fn is_doc_reachable(&self, did: DefId) -> bool { pub fn run_core(search_paths: SearchPaths, cfgs: Vec, - externs: Externs, + externs: config::Externs, input: Input, triple: Option) -> (clean::Crate, RenderInfo) { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index d0c4f126550ca177356b4032497309372d3cb436..255e6b1e786dfd38f621eaee0d7d318a6519a45e 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -51,7 +51,7 @@ extern crate serialize as rustc_serialize; // used by deriving -use std::collections::HashMap; +use std::collections::{BTreeMap, BTreeSet}; use std::default::Default; use std::env; use std::path::PathBuf; @@ -60,7 +60,8 @@ use externalfiles::ExternalHtml; use rustc::session::search_paths::SearchPaths; -use rustc::session::config::{ErrorOutputType, RustcOptGroup, nightly_options}; +use rustc::session::config::{ErrorOutputType, RustcOptGroup, nightly_options, + Externs}; #[macro_use] pub mod externalfiles; @@ -323,7 +324,7 @@ pub fn main_args(args: &[String]) -> isize { /// Looks inside the command line arguments to extract the relevant input format /// and files and then generates the necessary rustdoc output for formatting. fn acquire_input(input: &str, - externs: core::Externs, + externs: Externs, matches: &getopts::Matches) -> Result { match matches.opt_str("r").as_ref().map(|s| &**s) { Some("rust") => Ok(rust_input(input, externs, matches)), @@ -335,10 +336,10 @@ fn acquire_input(input: &str, } /// Extracts `--extern CRATE=PATH` arguments from `matches` and -/// returns a `HashMap` mapping crate names to their paths or else an +/// returns a map mapping crate names to their paths or else an /// error message. -fn parse_externs(matches: &getopts::Matches) -> Result { - let mut externs = HashMap::new(); +fn parse_externs(matches: &getopts::Matches) -> Result { + let mut externs = BTreeMap::new(); for arg in &matches.opt_strs("extern") { let mut parts = arg.splitn(2, '='); let name = parts.next().ok_or("--extern value must not be empty".to_string())?; @@ -346,9 +347,9 @@ fn parse_externs(matches: &getopts::Matches) -> Result { .ok_or("--extern value must be of the format `foo=bar`" .to_string())?; let name = name.to_string(); - externs.entry(name).or_insert(vec![]).push(location.to_string()); + externs.entry(name).or_insert_with(BTreeSet::new).insert(location.to_string()); } - Ok(externs) + Ok(Externs::new(externs)) } /// Interprets the input file as a rust source file, passing it through the @@ -356,7 +357,7 @@ fn parse_externs(matches: &getopts::Matches) -> Result { /// generated from the cleaned AST of the crate. /// /// This form of input will run all of the plug/cleaning passes -fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matches) -> Output { +fn rust_input(cratefile: &str, externs: Externs, matches: &getopts::Matches) -> Output { let mut default_passes = !matches.opt_present("no-defaults"); let mut passes = matches.opt_strs("passes"); let mut plugins = matches.opt_strs("plugins"); diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index d21726dd40f08195ec366b989025a38c5b47b331..1421a3c78fc5a40a7c4803b2cc21ff73677cc4c4 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -14,10 +14,10 @@ use std::io; use std::path::{PathBuf, Path}; -use core; use getopts; use testing; use rustc::session::search_paths::SearchPaths; +use rustc::session::config::Externs; use externalfiles::ExternalHtml; @@ -142,7 +142,7 @@ pub fn render(input: &str, mut output: PathBuf, matches: &getopts::Matches, } /// Run any tests/code examples in the markdown file `input`. -pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: core::Externs, +pub fn test(input: &str, cfgs: Vec, libs: SearchPaths, externs: Externs, mut test_args: Vec) -> isize { let input_str = load_or_return!(input, 1, 2); diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 5f1d28c8d316ad35014bd2f4a56f124449a6fdfc..12749c857ab91f1321b340315f2a10d74a4da179 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -26,7 +26,8 @@ use rustc::dep_graph::DepGraph; use rustc::hir::map as hir_map; use rustc::session::{self, config}; -use rustc::session::config::{get_unstable_features_setting, OutputType}; +use rustc::session::config::{get_unstable_features_setting, OutputType, + OutputTypes, Externs}; use rustc::session::search_paths::{SearchPaths, PathKind}; use rustc_back::dynamic_lib::DynamicLibrary; use rustc_back::tempdir::TempDir; @@ -55,7 +56,7 @@ pub struct TestOptions { pub fn run(input: &str, cfgs: Vec, libs: SearchPaths, - externs: core::Externs, + externs: Externs, mut test_args: Vec, crate_name: Option) -> isize { @@ -172,7 +173,7 @@ fn scrape_test_config(krate: &::rustc::hir::Crate) -> TestOptions { } fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, - externs: core::Externs, + externs: Externs, should_panic: bool, no_run: bool, as_test_harness: bool, compile_fail: bool, mut error_codes: Vec, opts: &TestOptions) { // the test harness wants its own `main` & top level functions, so @@ -182,8 +183,7 @@ fn runtest(test: &str, cratename: &str, cfgs: Vec, libs: SearchPaths, name: driver::anon_src(), input: test.to_owned(), }; - let mut outputs = HashMap::new(); - outputs.insert(OutputType::Exe, None); + let outputs = OutputTypes::new(&[(OutputType::Exe, None)]); let sessopts = config::Options { maybe_sysroot: Some(env::current_exe().unwrap().parent().unwrap() @@ -396,7 +396,7 @@ pub struct Collector { names: Vec, cfgs: Vec, libs: SearchPaths, - externs: core::Externs, + externs: Externs, cnt: usize, use_headers: bool, current_header: Option, @@ -405,7 +405,7 @@ pub struct Collector { } impl Collector { - pub fn new(cratename: String, cfgs: Vec, libs: SearchPaths, externs: core::Externs, + pub fn new(cratename: String, cfgs: Vec, libs: SearchPaths, externs: Externs, use_headers: bool, opts: TestOptions) -> Collector { Collector { tests: Vec::new(), diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 29da0fb1a2735aa14c4ac71633ac9be97f9b9ebb..d7a6c16ede0ce3e6a2ba4fde15de6aec48ed415a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1171,7 +1171,7 @@ pub fn check_crate(krate: &ast::Crate, visit::walk_crate(&mut PostExpansionVisitor { context: &ctx }, krate); } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum UnstableFeatures { /// Hard errors for unstable features are active, as on /// beta/stable channels. diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs index d5220316a20ff083381235e0bd499cff04721629..f501c06a823b5d03ec83826e2b86d81ad488199a 100644 --- a/src/test/run-make/issue-19371/foo.rs +++ b/src/test/run-make/issue-19371/foo.rs @@ -19,7 +19,8 @@ use rustc::dep_graph::DepGraph; use rustc::session::{build_session, Session}; -use rustc::session::config::{basic_options, build_configuration, Input, OutputType}; +use rustc::session::config::{basic_options, build_configuration, Input, + OutputType, OutputTypes}; use rustc_driver::driver::{compile_input, CompileController, anon_src}; use rustc_metadata::cstore::CStore; use rustc_errors::registry::Registry; @@ -51,7 +52,7 @@ fn main() {} fn basic_sess(sysroot: PathBuf) -> (Session, Rc) { let mut opts = basic_options(); - opts.output_types.insert(OutputType::Exe, None); + opts.output_types = OutputTypes::new([(OutputType::Exe, None)]); opts.maybe_sysroot = Some(sysroot); let descriptions = Registry::new(&rustc::DIAGNOSTICS);