]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc/session/config.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc / session / config.rs
index 6fb8c03370142713f7c935fee8a1765e53af2a0b..e988ddcd97b1518e34adde4771ca071903db99d6 100644 (file)
@@ -14,7 +14,6 @@
 pub use self::EntryFnType::*;
 pub use self::CrateType::*;
 pub use self::Passes::*;
-pub use self::OptLevel::*;
 pub use self::DebugInfoLevel::*;
 
 use session::{early_error, early_warn, Session};
@@ -27,41 +26,49 @@ use middle::cstore;
 use syntax::ast::{self, IntTy, UintTy};
 use syntax::attr;
 use syntax::attr::AttrMetaMethods;
-use syntax::diagnostic::{ColorConfig, Auto, Always, Never, SpanHandler};
 use syntax::parse;
 use syntax::parse::token::InternedString;
 use syntax::feature_gate::UnstableFeatures;
 
+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;
 
-use llvm;
-
 pub struct Config {
     pub target: Target,
     pub int_type: IntTy,
     pub uint_type: UintTy,
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Hash)]
 pub enum OptLevel {
     No, // -O0
     Less, // -O1
     Default, // -O2
-    Aggressive // -O3
+    Aggressive, // -O3
+    Size, // -Os
+    SizeMin, // -Oz
 }
 
-#[derive(Clone, Copy, PartialEq)]
+#[derive(Clone, Copy, PartialEq, Hash)]
 pub enum DebugInfoLevel {
     NoDebugInfo,
     LimitedDebugInfo,
     FullDebugInfo,
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, PartialOrd, Ord,
+         RustcEncodable, RustcDecodable)]
 pub enum OutputType {
     Bitcode,
     Assembly,
@@ -71,6 +78,18 @@ pub enum OutputType {
     DepInfo,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ErrorOutputType {
+    HumanReadable(ColorConfig),
+    Json,
+}
+
+impl Default for ErrorOutputType {
+    fn default() -> ErrorOutputType {
+        ErrorOutputType::HumanReadable(ColorConfig::Auto)
+    }
+}
+
 impl OutputType {
     fn is_compatible_with_codegen_units_and_single_output_file(&self) -> bool {
         match *self {
@@ -93,66 +112,208 @@ impl OutputType {
             OutputType::DepInfo => "dep-info",
         }
     }
+
+    pub fn extension(&self) -> &'static str {
+        match *self {
+            OutputType::Bitcode => "bc",
+            OutputType::Assembly => "s",
+            OutputType::LlvmAssembly => "ll",
+            OutputType::Object => "o",
+            OutputType::DepInfo => "d",
+            OutputType::Exe => "",
+        }
+    }
 }
 
-#[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<CrateType>,
-
-    pub gc: bool,
-    pub optimize: OptLevel,
-    pub debug_assertions: bool,
-    pub debuginfo: DebugInfoLevel,
-    pub lint_opts: Vec<(String, lint::Level)>,
-    pub lint_cap: Option<lint::Level>,
-    pub describe_lints: bool,
-    pub output_types: HashMap<OutputType, Option<PathBuf>>,
-    // 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<PathBuf>,
-    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 treat_err_as_bug: bool,
-    pub no_analysis: bool,
-    pub debugging_opts: DebuggingOptions,
-    pub prints: Vec<PrintRequest>,
-    pub cg: CodegenOptions,
-    pub color: ColorConfig,
-    pub show_span: Option<String>,
-    pub externs: HashMap<String, Vec<String>>,
-    pub crate_name: Option<String>,
-    /// 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<String>,
-    /// 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<OutputType, Option<PathBuf>>);
+
+impl OutputTypes {
+    pub fn new(entries: &[(OutputType, Option<PathBuf>)]) -> OutputTypes {
+        OutputTypes(BTreeMap::from_iter(entries.iter()
+                                               .map(|&(k, ref v)| (k, v.clone()))))
+    }
+
+    pub fn get(&self, key: &OutputType) -> Option<&Option<PathBuf>> {
+        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<PathBuf>> {
+        self.0.keys()
+    }
+
+    pub fn values<'a>(&'a self) -> BTreeMapValuesIter<'a, OutputType, Option<PathBuf>> {
+        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<String, BTreeSet<String>>);
+
+impl Externs {
+    pub fn new(data: BTreeMap<String, BTreeSet<String>>) -> Externs {
+        Externs(data)
+    }
+
+    pub fn get(&self, key: &str) -> Option<&BTreeSet<String>> {
+        self.0.get(key)
+    }
+
+    pub fn iter<'a>(&'a self) -> BTreeMapIter<'a, String, BTreeSet<String>> {
+        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<CrateType> [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<lint::Level> [TRACKED],
+        describe_lints: bool [UNTRACKED],
+        output_types: OutputTypes [TRACKED],
+        // FIXME(mw): We track this for now but it actually doesn't make too
+        //            much sense: The search path can stay the same while the
+        //            things discovered there might have changed on disk.
+        search_paths: SearchPaths [TRACKED],
+        libs: Vec<(String, cstore::NativeLibraryKind)> [TRACKED],
+        maybe_sysroot: Option<PathBuf> [TRACKED],
+
+        target_triple: String [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<PathBuf> [UNTRACKED],
+
+        debugging_opts: DebuggingOptions [TRACKED],
+        prints: Vec<PrintRequest> [UNTRACKED],
+        cg: CodegenOptions [TRACKED],
+        // FIXME(mw): We track this for now but it actually doesn't make too
+        //            much sense: The value of this option can stay the same
+        //            while the files they refer to might have changed on disk.
+        externs: Externs [TRACKED],
+        crate_name: Option<String> [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<String> [TRACKED],
+        // Indicates how the compiler should treat unstable features
+        unstable_features: UnstableFeatures [TRACKED],
+    }
+);
+
 #[derive(Clone, PartialEq, Eq)]
 pub enum PrintRequest {
     FileNames,
     Sysroot,
     CrateName,
+    Cfg,
+    TargetList,
+    TargetCPUs,
+    TargetFeatures,
+    RelocationModels,
+    CodeModels,
 }
 
 pub enum Input {
     /// Load source from file
     File(PathBuf),
-    /// The string is the source
-    Str(String)
+    Str {
+        /// String that is shown in place of a filename
+        name: String,
+        /// Anonymous source string
+        input: String,
+    },
 }
 
 impl Input {
@@ -160,7 +321,7 @@ impl Input {
         match *self {
             Input::File(ref ifile) => ifile.file_stem().unwrap()
                                            .to_str().unwrap().to_string(),
-            Input::Str(_) => "rust_out".to_string(),
+            Input::Str { .. } => "rust_out".to_string(),
         }
     }
 }
@@ -171,26 +332,65 @@ pub struct OutputFilenames {
     pub out_filestem: String,
     pub single_output_file: Option<PathBuf>,
     pub extra: String,
-    pub outputs: HashMap<OutputType, Option<PathBuf>>,
+    pub outputs: OutputTypes,
 }
 
+/// Codegen unit names generated by the numbered naming scheme will contain this
+/// marker right before the index of the codegen unit.
+pub const NUMBERED_CODEGEN_UNIT_MARKER: &'static str = ".cgu-";
+
 impl OutputFilenames {
     pub fn path(&self, flavor: OutputType) -> PathBuf {
         self.outputs.get(&flavor).and_then(|p| p.to_owned())
             .or_else(|| self.single_output_file.clone())
-            .unwrap_or_else(|| self.temp_path(flavor))
+            .unwrap_or_else(|| self.temp_path(flavor, None))
     }
 
-    pub fn temp_path(&self, flavor: OutputType) -> PathBuf {
+    /// Get the path where a compilation artifact of the given type for the
+    /// given codegen unit should be placed on disk. If codegen_unit_name is
+    /// None, a path distinct from those of any codegen unit will be generated.
+    pub fn temp_path(&self,
+                     flavor: OutputType,
+                     codegen_unit_name: Option<&str>)
+                     -> PathBuf {
+        let extension = flavor.extension();
+        self.temp_path_ext(extension, codegen_unit_name)
+    }
+
+    /// Like temp_path, but also supports things where there is no corresponding
+    /// OutputType, like no-opt-bitcode or lto-bitcode.
+    pub fn temp_path_ext(&self,
+                         ext: &str,
+                         codegen_unit_name: Option<&str>)
+                         -> PathBuf {
         let base = self.out_directory.join(&self.filestem());
-        match flavor {
-            OutputType::Bitcode => base.with_extension("bc"),
-            OutputType::Assembly => base.with_extension("s"),
-            OutputType::LlvmAssembly => base.with_extension("ll"),
-            OutputType::Object => base.with_extension("o"),
-            OutputType::DepInfo => base.with_extension("d"),
-            OutputType::Exe => base,
+
+        let mut extension = String::new();
+
+        if let Some(codegen_unit_name) = codegen_unit_name {
+            if codegen_unit_name.contains(NUMBERED_CODEGEN_UNIT_MARKER) {
+                // If we use the numbered naming scheme for modules, we don't want
+                // the files to look like <crate-name><extra>.<crate-name>.<index>.<ext>
+                // but simply <crate-name><extra>.<index>.<ext>
+                let marker_offset = codegen_unit_name.rfind(NUMBERED_CODEGEN_UNIT_MARKER)
+                                                     .unwrap();
+                let index_offset = marker_offset + NUMBERED_CODEGEN_UNIT_MARKER.len();
+                extension.push_str(&codegen_unit_name[index_offset .. ]);
+            } else {
+                extension.push_str(codegen_unit_name);
+            };
         }
+
+        if !ext.is_empty() {
+            if !extension.is_empty() {
+                extension.push_str(".");
+            }
+
+            extension.push_str(ext);
+        }
+
+        let path = base.with_extension(&extension[..]);
+        path
     }
 
     pub fn with_extension(&self, extension: &str) -> PathBuf {
@@ -219,28 +419,23 @@ pub fn host_triple() -> &'static str {
 pub fn basic_options() -> Options {
     Options {
         crate_types: Vec::new(),
-        gc: false,
-        optimize: No,
+        optimize: OptLevel::No,
         debuginfo: NoDebugInfo,
         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,
-        no_analysis: false,
+        mir_opt_level: 1,
+        incremental: None,
         debugging_opts: basic_debugging_options(),
         prints: Vec::new(),
         cg: basic_codegen_options(),
-        color: Auto,
-        show_span: None,
-        externs: HashMap::new(),
+        error_format: ErrorOutputType::default(),
+        externs: Externs(BTreeMap::new()),
         crate_name: None,
         alt_std_name: None,
         libs: Vec::new(),
@@ -249,6 +444,20 @@ pub fn basic_options() -> Options {
     }
 }
 
+impl Options {
+    /// True if there is a reason to build the dep graph.
+    pub fn build_dep_graph(&self) -> bool {
+        self.incremental.is_some() ||
+            self.debugging_opts.dump_dep_graph ||
+            self.debugging_opts.query_dep_graph
+    }
+
+    pub fn single_codegen_unit(&self) -> bool {
+        self.incremental.is_none() ||
+        self.cg.codegen_units == 1
+    }
+}
+
 // The type of entry function, so
 // users can have their own entry
 // functions that don't start a
@@ -266,9 +475,10 @@ pub enum CrateType {
     CrateTypeDylib,
     CrateTypeRlib,
     CrateTypeStaticlib,
+    CrateTypeCdylib,
 }
 
-#[derive(Clone)]
+#[derive(Clone, Hash)]
 pub enum Passes {
     SomePasses(Vec<String>),
     AllPasses,
@@ -283,6 +493,21 @@ impl Passes {
     }
 }
 
+#[derive(Clone, PartialEq, Hash)]
+pub enum PanicStrategy {
+    Unwind,
+    Abort,
+}
+
+impl PanicStrategy {
+    pub fn desc(&self) -> &str {
+        match *self {
+            PanicStrategy::Unwind => "unwind",
+            PanicStrategy::Abort => "abort",
+        }
+    }
+}
+
 /// Declare a macro that will define all CodegenOptions/DebuggingOptions fields and parsers all
 /// at once. The goal of this macro is to define an interface that can be
 /// programmatically used by the option parser in order to initialize the struct
@@ -297,7 +522,12 @@ macro_rules! options {
     ($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),* }
@@ -306,7 +536,7 @@ macro_rules! options {
         $struct_name { $($opt: $init),* }
     }
 
-    pub fn $buildfn(matches: &getopts::Matches, color: ColorConfig) -> $struct_name
+    pub fn $buildfn(matches: &getopts::Matches, error_format: ErrorOutputType) -> $struct_name
     {
         let mut op = $defaultfn();
         for option in matches.opt_strs($prefix) {
@@ -320,35 +550,51 @@ macro_rules! options {
                 if !setter(&mut op, value) {
                     match (value, opt_type_desc) {
                         (Some(..), None) => {
-                            early_error(color, &format!("{} option `{}` takes no \
-                                                         value", $outputname, key))
+                            early_error(error_format, &format!("{} option `{}` takes no \
+                                                              value", $outputname, key))
                         }
                         (None, Some(type_desc)) => {
-                            early_error(color, &format!("{0} option `{1}` requires \
-                                                         {2} ({3} {1}=<value>)",
-                                                        $outputname, key,
-                                                        type_desc, $prefix))
+                            early_error(error_format, &format!("{0} option `{1}` requires \
+                                                              {2} ({3} {1}=<value>)",
+                                                             $outputname, key,
+                                                             type_desc, $prefix))
                         }
                         (Some(value), Some(type_desc)) => {
-                            early_error(color, &format!("incorrect value `{}` for {} \
-                                                         option `{}` - {} was expected",
-                                                        value, $outputname,
-                                                        key, type_desc))
+                            early_error(error_format, &format!("incorrect value `{}` for {} \
+                                                              option `{}` - {} was expected",
+                                                             value, $outputname,
+                                                             key, type_desc))
                         }
-                        (None, None) => unreachable!()
+                        (None, None) => bug!()
                     }
                 }
                 found = true;
                 break;
             }
             if !found {
-                early_error(color, &format!("unknown {} option: `{}`",
-                                            $outputname, key));
+                early_error(error_format, &format!("unknown {} option: `{}`",
+                                                 $outputname, key));
             }
         }
         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)] =
@@ -359,6 +605,8 @@ macro_rules! options {
         pub const parse_bool: Option<&'static str> = None;
         pub const parse_opt_bool: Option<&'static str> =
             Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`");
+        pub const parse_all_bool: Option<&'static str> =
+            Some("one of: `y`, `yes`, `on`, `n`, `no`, or `off`");
         pub const parse_string: Option<&'static str> = Some("a string");
         pub const parse_opt_string: Option<&'static str> = Some("a string");
         pub const parse_list: Option<&'static str> = Some("a space-separated list of strings");
@@ -368,11 +616,13 @@ macro_rules! options {
             Some("a space-separated list of passes, or `all`");
         pub const parse_opt_uint: Option<&'static str> =
             Some("a number");
+        pub const parse_panic_strategy: Option<&'static str> =
+            Some("either `panic` or `abort`");
     }
 
     #[allow(dead_code)]
     mod $mod_set {
-        use super::{$struct_name, Passes, SomePasses, AllPasses};
+        use super::{$struct_name, Passes, SomePasses, AllPasses, PanicStrategy};
 
         $(
             pub fn $opt(cg: &mut $struct_name, v: Option<&str>) -> bool {
@@ -406,6 +656,25 @@ macro_rules! options {
             }
         }
 
+        fn parse_all_bool(slot: &mut bool, v: Option<&str>) -> bool {
+            match v {
+                Some(s) => {
+                    match s {
+                        "n" | "no" | "off" => {
+                            *slot = false;
+                        }
+                        "y" | "yes" | "on" => {
+                            *slot = true;
+                        }
+                        _ => { return false; }
+                    }
+
+                    true
+                },
+                None => { *slot = true; true }
+            }
+        }
+
         fn parse_opt_string(slot: &mut Option<String>, v: Option<&str>) -> bool {
             match v {
                 Some(s) => { *slot = Some(s.to_string()); true },
@@ -476,152 +745,193 @@ macro_rules! options {
                 }
             }
         }
+
+        fn parse_panic_strategy(slot: &mut PanicStrategy, v: Option<&str>) -> bool {
+            match v {
+                Some("unwind") => *slot = PanicStrategy::Unwind,
+                Some("abort") => *slot = PanicStrategy::Abort,
+                _ => return false
+            }
+            true
+        }
     }
 ) }
 
 options! {CodegenOptions, CodegenSetter, basic_codegen_options,
          build_codegen_options, "C", "codegen",
          CG_OPTIONS, cg_type_desc, cgsetters,
-    ar: Option<String> = (None, parse_opt_string,
+    ar: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "tool to assemble archives with"),
-    linker: Option<String> = (None, parse_opt_string,
+    linker: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "system linker to link outputs with"),
-    link_args: Option<Vec<String>> = (None, parse_opt_list,
+    link_args: Option<Vec<String>> = (None, parse_opt_list, [UNTRACKED],
         "extra arguments to pass to the linker (space separated)"),
-    lto: 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, [TRACKED],
         "perform LLVM link-time optimizations"),
-    target_cpu: Option<String> = (None, parse_opt_string,
-        "select target processor (llc -mcpu=help for details)"),
-    target_feature: String = ("".to_string(), parse_string,
-        "target specific attributes (llc -mattr=help for details)"),
-    passes: Vec<String> = (Vec::new(), parse_list,
+    target_cpu: Option<String> = (None, parse_opt_string, [TRACKED],
+        "select target processor (rustc --print target-cpus for details)"),
+    target_feature: String = ("".to_string(), parse_string, [TRACKED],
+        "target specific attributes (rustc --print target-features for details)"),
+    passes: Vec<String> = (Vec::new(), parse_list, [TRACKED],
         "a list of extra LLVM passes to run (space separated)"),
-    llvm_args: Vec<String> = (Vec::new(), parse_list,
+    llvm_args: Vec<String> = (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<bool> = (None, parse_opt_bool,
+    no_redzone: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "disable the use of the redzone"),
-    relocation_model: Option<String> = (None, parse_opt_string,
-         "choose the relocation model to use (llc -relocation-model for details)"),
-    code_model: Option<String> = (None, parse_opt_string,
-         "choose the code model to use (llc -code-model for details)"),
-    metadata: Vec<String> = (Vec::new(), parse_list,
+    relocation_model: Option<String> = (None, parse_opt_string, [TRACKED],
+         "choose the relocation model to use (rustc --print relocation-models for details)"),
+    code_model: Option<String> = (None, parse_opt_string, [TRACKED],
+         "choose the code model to use (rustc --print code-models for details)"),
+    metadata: Vec<String> = (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<usize> = (None, parse_opt_uint,
+    debuginfo: Option<usize> = (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<usize> = (None, parse_opt_uint,
-        "optimize with possible levels 0-3"),
-    debug_assertions: Option<bool> = (None, parse_opt_bool,
+    opt_level: Option<String> = (None, parse_opt_string, [TRACKED],
+        "optimize with possible levels 0-3, s, or z"),
+    debug_assertions: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "explicitly enable the cfg(debug_assertions) directive"),
-    inline_threshold: Option<usize> = (None, parse_opt_uint,
+    inline_threshold: Option<usize> = (None, parse_opt_uint, [TRACKED],
         "set the inlining threshold for"),
+    panic: PanicStrategy = (PanicStrategy::Unwind, parse_panic_strategy,
+        [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"),
-    count_type_sizes: bool = (false, parse_bool,
-        "count the sizes of aggregate types"),
-    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"),
-    gc: bool = (false, parse_bool,
-        "garbage collect shared data (experimental)"),
-    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,
-        "write syntax and type analysis information in addition to normal output"),
-    print_move_fragments: 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, [UNTRACKED],
+        "write syntax and type analysis (in CSV format) information in addition to normal output"),
+    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"),
-    no_analysis: bool = (false, parse_bool,
+    continue_parse_after_error: bool = (false, parse_bool, [TRACKED],
+          "attempt to recover from parse errors (experimental)"),
+    incremental: Option<String> = (None, parse_opt_string, [UNTRACKED],
+          "enable incremental compilation (experimental)"),
+    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, [UNTRACKED],
+          "dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv)"),
+    query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
+          "enable queries of the dependency graph for regression testing"),
+    no_analysis: bool = (false, parse_bool, [UNTRACKED],
           "parse and expand the source, but run no analysis"),
-    extra_plugins: Vec<String> = (Vec::new(), parse_list,
+    extra_plugins: Vec<String> = (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"),
-    print_enum_sizes: bool = (false, parse_bool,
-          "print the size of enums and their variants"),
-    force_overflow_checks: Option<bool> = (None, parse_opt_bool,
+    force_overflow_checks: Option<bool> = (None, parse_opt_bool, [TRACKED],
           "force overflow checks on or off"),
-    force_dropflag_checks: Option<bool> = (None, parse_opt_bool,
+    force_dropflag_checks: Option<bool> = (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_mtwt_tables: bool = (false, parse_bool,
-          "don't clear the resolution tables after analysis"),
+    keep_hygiene_data: bool = (false, parse_bool, [UNTRACKED],
+          "don't clear the hygiene data after analysis"),
+    keep_ast: bool = (false, parse_bool, [UNTRACKED],
+          "keep the AST after lowering it to HIR"),
+    show_span: Option<String> = (None, parse_opt_string, [TRACKED],
+          "show spans for compiler debugging (expr|pat|ty)"),
+    print_trans_items: Option<String> = (None, parse_opt_string, [UNTRACKED],
+          "print the result of the translation item collection pass"),
+    mir_opt_level: Option<usize> = (None, parse_opt_uint, [TRACKED],
+          "set the MIR optimization level (0-3)"),
+    dump_mir: Option<String> = (None, parse_opt_string, [UNTRACKED],
+          "dump MIR state at various points in translation"),
+    dump_mir_dir: Option<String> = (None, parse_opt_string, [UNTRACKED],
+          "the directory the MIR is dumped into"),
+    orbit: bool = (true, parse_all_bool, [UNTRACKED],
+          "get MIR where it belongs - everywhere; most importantly, in orbit"),
 }
 
 pub fn default_lib_output() -> CrateType {
@@ -637,6 +947,7 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
     let os = &sess.target.target.target_os;
     let env = &sess.target.target.target_env;
     let vendor = &sess.target.target.target_vendor;
+    let max_atomic_width = sess.target.target.options.max_atomic_width;
 
     let fam = if let Some(ref fam) = sess.target.target.options.target_family {
         intern(fam)
@@ -660,6 +971,18 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
         "windows" | "unix" => ret.push(attr::mk_word_item(fam)),
         _ => (),
     }
+    if sess.target.target.options.has_elf_tls {
+        ret.push(attr::mk_word_item(InternedString::new("target_thread_local")));
+    }
+    for &i in &[8, 16, 32, 64, 128] {
+        if i <= max_atomic_width {
+            let s = i.to_string();
+            ret.push(mk(InternedString::new("target_has_atomic"), intern(&s)));
+            if &s == wordsz {
+                ret.push(mk(InternedString::new("target_has_atomic"), intern("ptr")));
+            }
+        }
+    }
     if sess.opts.debug_assertions {
         ret.push(attr::mk_word_item(InternedString::new("debug_assertions")));
     }
@@ -673,11 +996,12 @@ pub fn append_configuration(cfg: &mut ast::CrateConfig,
     }
 }
 
-pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
+pub fn build_configuration(sess: &Session,
+                           mut user_cfg: ast::CrateConfig)
+                           -> ast::CrateConfig {
     // Combine the configuration requested by the session (command line) with
     // some default and generated configuration items
     let default_cfg = default_configuration(sess);
-    let mut user_cfg = sess.opts.cfg.clone();
     // If the user wants a test runner, then add the test cfg
     if sess.opts.test {
         append_configuration(&mut user_cfg, InternedString::new("test"))
@@ -687,19 +1011,23 @@ pub fn build_configuration(sess: &Session) -> ast::CrateConfig {
     v
 }
 
-pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config {
+pub fn build_target_config(opts: &Options, sp: &Handler) -> Config {
     let target = match Target::search(&opts.target_triple) {
         Ok(t) => t,
         Err(e) => {
-            panic!(sp.handler().fatal(&format!("Error loading target specification: {}", e)));
+            sp.struct_fatal(&format!("Error loading target specification: {}", e))
+                .help("Use `--print target-list` for a list of built-in targets")
+                .emit();
+            panic!(FatalError);
         }
     };
 
     let (int_type, uint_type) = match &target.target_pointer_width[..] {
-        "32" => (ast::TyI32, ast::TyU32),
-        "64" => (ast::TyI64, ast::TyU64),
-        w    => panic!(sp.handler().fatal(&format!("target specification was invalid: \
-                                                    unrecognized target-pointer-width {}", w))),
+        "16" => (ast::IntTy::I16, ast::UintTy::U16),
+        "32" => (ast::IntTy::I32, ast::UintTy::U32),
+        "64" => (ast::IntTy::I64, ast::UintTy::U64),
+        w    => panic!(sp.fatal(&format!("target specification was invalid: \
+                                          unrecognized target-pointer-width {}", w))),
     };
 
     Config {
@@ -709,24 +1037,20 @@ pub fn build_target_config(opts: &Options, sp: &SpanHandler) -> Config {
     }
 }
 
-/// Returns the "short" subset of the stable rustc command line options.
-pub fn short_optgroups() -> Vec<getopts::OptGroup> {
-    rustc_short_optgroups().into_iter()
-        .filter(|g|g.is_stable())
-        .map(|g|g.opt_group)
-        .collect()
-}
-
-/// Returns all of the stable rustc command line options.
-pub fn optgroups() -> Vec<getopts::OptGroup> {
-    rustc_optgroups().into_iter()
-        .filter(|g|g.is_stable())
-        .map(|g|g.opt_group)
-        .collect()
-}
-
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum OptionStability { Stable, Unstable }
+pub enum OptionStability {
+    Stable,
+
+    // FIXME: historically there were some options which were either `-Z` or
+    //        required the `-Z unstable-options` flag, which were all intended
+    //        to be unstable. Unfortunately we didn't actually gate usage of
+    //        these options on the stable compiler, so we still allow them there
+    //        today. There are some warnings printed out about this in the
+    //        driver.
+    UnstableButNotReally,
+
+    Unstable,
+}
 
 #[derive(Clone, PartialEq, Eq)]
 pub struct RustcOptGroup {
@@ -739,13 +1063,21 @@ impl RustcOptGroup {
         self.stability == OptionStability::Stable
     }
 
-    fn stable(g: getopts::OptGroup) -> RustcOptGroup {
+    pub fn stable(g: getopts::OptGroup) -> RustcOptGroup {
         RustcOptGroup { opt_group: g, stability: OptionStability::Stable }
     }
 
-    fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
+    #[allow(dead_code)] // currently we have no "truly unstable" options
+    pub fn unstable(g: getopts::OptGroup) -> RustcOptGroup {
         RustcOptGroup { opt_group: g, stability: OptionStability::Unstable }
     }
+
+    fn unstable_bnr(g: getopts::OptGroup) -> RustcOptGroup {
+        RustcOptGroup {
+            opt_group: g,
+            stability: OptionStability::UnstableButNotReally,
+        }
+    }
 }
 
 // The `opt` local module holds wrappers around the `getopts` API that
@@ -767,24 +1099,57 @@ mod opt {
 
     fn stable(g: getopts::OptGroup) -> R { RustcOptGroup::stable(g) }
     fn unstable(g: getopts::OptGroup) -> R { RustcOptGroup::unstable(g) }
+    fn unstable_bnr(g: getopts::OptGroup) -> R { RustcOptGroup::unstable_bnr(g) }
 
-    // FIXME (pnkfelix): We default to stable since the current set of
-    // options is defacto stable.  However, it would be good to revise the
-    // code so that a stable option is the thing that takes extra effort
-    // to encode.
-
-    pub fn     opt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optopt(a, b, c, d)) }
-    pub fn   multi(a: S, b: S, c: S, d: S) -> R { stable(getopts::optmulti(a, b, c, d)) }
-    pub fn    flag(a: S, b: S, c: S)       -> R { stable(getopts::optflag(a, b, c)) }
-    pub fn flagopt(a: S, b: S, c: S, d: S) -> R { stable(getopts::optflagopt(a, b, c, d)) }
-    pub fn flagmulti(a: S, b: S, c: S)     -> R { stable(getopts::optflagmulti(a, b, c)) }
+    pub fn opt_s(a: S, b: S, c: S, d: S) -> R {
+        stable(getopts::optopt(a, b, c, d))
+    }
+    pub fn multi_s(a: S, b: S, c: S, d: S) -> R {
+        stable(getopts::optmulti(a, b, c, d))
+    }
+    pub fn flag_s(a: S, b: S, c: S) -> R {
+        stable(getopts::optflag(a, b, c))
+    }
+    pub fn flagopt_s(a: S, b: S, c: S, d: S) -> R {
+        stable(getopts::optflagopt(a, b, c, d))
+    }
+    pub fn flagmulti_s(a: S, b: S, c: S) -> R {
+        stable(getopts::optflagmulti(a, b, c))
+    }
 
+    pub fn opt(a: S, b: S, c: S, d: S) -> R {
+        unstable(getopts::optopt(a, b, c, d))
+    }
+    pub fn multi(a: S, b: S, c: S, d: S) -> R {
+        unstable(getopts::optmulti(a, b, c, d))
+    }
+    pub fn flag(a: S, b: S, c: S) -> R {
+        unstable(getopts::optflag(a, b, c))
+    }
+    pub fn flagopt(a: S, b: S, c: S, d: S) -> R {
+        unstable(getopts::optflagopt(a, b, c, d))
+    }
+    pub fn flagmulti(a: S, b: S, c: S) -> R {
+        unstable(getopts::optflagmulti(a, b, c))
+    }
 
-    pub fn     opt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optopt(a, b, c, d)) }
-    pub fn   multi_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optmulti(a, b, c, d)) }
-    pub fn    flag_u(a: S, b: S, c: S)       -> R { unstable(getopts::optflag(a, b, c)) }
-    pub fn flagopt_u(a: S, b: S, c: S, d: S) -> R { unstable(getopts::optflagopt(a, b, c, d)) }
-    pub fn flagmulti_u(a: S, b: S, c: S)     -> R { unstable(getopts::optflagmulti(a, b, c)) }
+    // Do not use these functions for any new options added to the compiler, all
+    // new options should use the `*_u` variants above to be truly unstable.
+    pub fn opt_ubnr(a: S, b: S, c: S, d: S) -> R {
+        unstable_bnr(getopts::optopt(a, b, c, d))
+    }
+    pub fn multi_ubnr(a: S, b: S, c: S, d: S) -> R {
+        unstable_bnr(getopts::optmulti(a, b, c, d))
+    }
+    pub fn flag_ubnr(a: S, b: S, c: S) -> R {
+        unstable_bnr(getopts::optflag(a, b, c))
+    }
+    pub fn flagopt_ubnr(a: S, b: S, c: S, d: S) -> R {
+        unstable_bnr(getopts::optflagopt(a, b, c, d))
+    }
+    pub fn flagmulti_ubnr(a: S, b: S, c: S) -> R {
+        unstable_bnr(getopts::optflagmulti(a, b, c))
+    }
 }
 
 /// Returns the "short" subset of the rustc command line options,
@@ -792,48 +1157,46 @@ mod opt {
 /// part of the stable long-term interface for rustc.
 pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
     vec![
-        opt::flag("h", "help", "Display this message"),
-        opt::multi("", "cfg", "Configure the compilation environment", "SPEC"),
-        opt::multi("L", "",   "Add a directory to the library search path",
-                   "[KIND=]PATH"),
-        opt::multi("l", "",   "Link the generated crate(s) to the specified native
-                             library NAME. The optional KIND can be one of,
+        opt::flag_s("h", "help", "Display this message"),
+        opt::multi_s("", "cfg", "Configure the compilation environment", "SPEC"),
+        opt::multi_s("L", "",   "Add a directory to the library search path. The
+                             optional KIND can be one of dependency, crate, native,
+                             framework or all (the default).", "[KIND=]PATH"),
+        opt::multi_s("l", "",   "Link the generated crate(s) to the specified native
+                             library NAME. The optional KIND can be one of
                              static, dylib, or framework. If omitted, dylib is
                              assumed.", "[KIND=]NAME"),
-        opt::multi("", "crate-type", "Comma separated list of types of crates
+        opt::multi_s("", "crate-type", "Comma separated list of types of crates
                                     for the compiler to emit",
-                   "[bin|lib|rlib|dylib|staticlib]"),
-        opt::opt("", "crate-name", "Specify the name of the crate being built",
+                   "[bin|lib|rlib|dylib|cdylib|staticlib]"),
+        opt::opt_s("", "crate-name", "Specify the name of the crate being built",
                "NAME"),
-        opt::multi("", "emit", "Comma separated list of types of output for \
+        opt::multi_s("", "emit", "Comma separated list of types of output for \
                               the compiler to emit",
                  "[asm|llvm-bc|llvm-ir|obj|link|dep-info]"),
-        opt::multi("", "print", "Comma separated list of compiler information to \
+        opt::multi_s("", "print", "Comma separated list of compiler information to \
                                print on stdout",
-                 "[crate-name|file-names|sysroot]"),
-        opt::flagmulti("g",  "",  "Equivalent to -C debuginfo=2"),
-        opt::flagmulti("O", "", "Equivalent to -C opt-level=2"),
-        opt::opt("o", "", "Write output to <filename>", "FILENAME"),
-        opt::opt("",  "out-dir", "Write output to compiler-chosen filename \
+                 "[crate-name|file-names|sysroot|cfg|target-list|target-cpus|\
+                   target-features|relocation-models|code-models]"),
+        opt::flagmulti_s("g",  "",  "Equivalent to -C debuginfo=2"),
+        opt::flagmulti_s("O", "", "Equivalent to -C opt-level=2"),
+        opt::opt_s("o", "", "Write output to <filename>", "FILENAME"),
+        opt::opt_s("",  "out-dir", "Write output to compiler-chosen filename \
                                 in <dir>", "DIR"),
-        opt::opt("", "explain", "Provide a detailed explanation of an error \
+        opt::opt_s("", "explain", "Provide a detailed explanation of an error \
                                message", "OPT"),
-        opt::flag("", "test", "Build a test harness"),
-        opt::opt("", "target", "Target triple cpu-manufacturer-kernel[-os] \
-                              to compile for (see chapter 3.4 of \
-                              http://www.sourceware.org/autobook/
-                              for details)",
-               "TRIPLE"),
-        opt::multi("W", "warn", "Set lint warnings", "OPT"),
-        opt::multi("A", "allow", "Set lint allowed", "OPT"),
-        opt::multi("D", "deny", "Set lint denied", "OPT"),
-        opt::multi("F", "forbid", "Set lint forbidden", "OPT"),
-        opt::multi("", "cap-lints", "Set the most restrictive lint level. \
+        opt::flag_s("", "test", "Build a test harness"),
+        opt::opt_s("", "target", "Target triple for which the code is compiled", "TARGET"),
+        opt::multi_s("W", "warn", "Set lint warnings", "OPT"),
+        opt::multi_s("A", "allow", "Set lint allowed", "OPT"),
+        opt::multi_s("D", "deny", "Set lint denied", "OPT"),
+        opt::multi_s("F", "forbid", "Set lint forbidden", "OPT"),
+        opt::multi_s("", "cap-lints", "Set the most restrictive lint level. \
                                      More restrictive lints are capped at this \
                                      level", "LEVEL"),
-        opt::multi("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
-        opt::flag("V", "version", "Print version info and exit"),
-        opt::flag("v", "verbose", "Use verbose output"),
+        opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
+        opt::flag_s("V", "version", "Print version info and exit"),
+        opt::flag_s("v", "verbose", "Use verbose output"),
     ]
 }
 
@@ -843,31 +1206,40 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
 pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
     let mut opts = rustc_short_optgroups();
     opts.extend_from_slice(&[
-        opt::multi("", "extern", "Specify where an external rust library is \
-                                located",
-                 "NAME=PATH"),
-        opt::opt("", "sysroot", "Override the system root", "PATH"),
-        opt::multi("Z", "", "Set internal debugging options", "FLAG"),
-        opt::opt("", "color", "Configure coloring of output:
-            auto   = colorize, if output goes to a tty (default);
-            always = always colorize output;
-            never  = never colorize output", "auto|always|never"),
-
-        opt::flagopt_u("", "pretty",
-                   "Pretty-print the input instead of compiling;
-                   valid types are: `normal` (un-annotated source),
-                   `expanded` (crates expanded), or
-                   `expanded,identified` (fully parenthesized, AST nodes with IDs).",
-                 "TYPE"),
-        opt::flagopt_u("", "unpretty",
-                     "Present the input source, unstable (and less-pretty) variants;
-                      valid types are any of the types for `--pretty`, as well as:
-                      `flowgraph=<nodeid>` (graphviz formatted flowgraph for node),
-                      `everybody_loops` (all function bodies replaced with `loop {}`),
-                      `hir` (the HIR), `hir,identified`, or
-                      `hir,typed` (HIR with types for each node).",
-                     "TYPE"),
-        opt::opt_u("", "show-span", "Show spans for compiler debugging", "expr|pat|ty"),
+        opt::multi_s("", "extern", "Specify where an external rust library is located",
+                     "NAME=PATH"),
+        opt::opt_s("", "sysroot", "Override the system root", "PATH"),
+        opt::multi_ubnr("Z", "", "Set internal debugging options", "FLAG"),
+        opt::opt_s("", "error-format",
+                      "How errors and other messages are produced",
+                      "human|json"),
+        opt::opt_s("", "color", "Configure coloring of output:
+                                 auto   = colorize, if output goes to a tty (default);
+                                 always = always colorize output;
+                                 never  = never colorize output", "auto|always|never"),
+
+        opt::flagopt_ubnr("", "pretty",
+                          "Pretty-print the input instead of compiling;
+                           valid types are: `normal` (un-annotated source),
+                           `expanded` (crates expanded), or
+                           `expanded,identified` (fully parenthesized, AST nodes with IDs).",
+                          "TYPE"),
+        opt::flagopt_ubnr("", "unpretty",
+                          "Present the input source, unstable (and less-pretty) variants;
+                           valid types are any of the types for `--pretty`, as well as:
+                           `flowgraph=<nodeid>` (graphviz formatted flowgraph for node),
+                           `everybody_loops` (all function bodies replaced with `loop {}`),
+                           `hir` (the HIR), `hir,identified`, or
+                           `hir,typed` (HIR with types for each node).",
+                          "TYPE"),
+
+        // new options here should **not** use the `_ubnr` functions, all new
+        // unstable options should use the short variants to indicate that they
+        // are truly unstable. All `_ubnr` flags are just that way because they
+        // were so historically.
+        //
+        // You may also wish to keep this comment at the bottom of this list to
+        // ensure that others see it.
     ]);
     opts
 }
@@ -875,31 +1247,63 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
 // Convert strings provided as --cfg [cfgspec] into a crate_cfg
 pub fn parse_cfgspecs(cfgspecs: Vec<String> ) -> ast::CrateConfig {
     cfgspecs.into_iter().map(|s| {
-        parse::parse_meta_from_source_str("cfgspec".to_string(),
-                                          s.to_string(),
-                                          Vec::new(),
-                                          &parse::ParseSess::new())
+        let sess = parse::ParseSess::new();
+        let mut parser = parse::new_parser_from_source_str(&sess,
+                                                           Vec::new(),
+                                                           "cfgspec".to_string(),
+                                                           s.to_string());
+        let meta_item = panictry!(parser.parse_meta_item());
+
+        if !parser.reader.is_eof() {
+            early_error(ErrorOutputType::default(), &format!("invalid --cfg argument: {}",
+                                                             s))
+        }
+
+        meta_item
     }).collect::<ast::CrateConfig>()
 }
 
-pub fn build_session_options(matches: &getopts::Matches) -> Options {
+pub fn build_session_options_and_crate_config(matches: &getopts::Matches)
+                                              -> (Options, ast::CrateConfig) {
     let color = match matches.opt_str("color").as_ref().map(|s| &s[..]) {
-        Some("auto")   => Auto,
-        Some("always") => Always,
-        Some("never")  => Never,
+        Some("auto")   => ColorConfig::Auto,
+        Some("always") => ColorConfig::Always,
+        Some("never")  => ColorConfig::Never,
 
-        None => Auto,
+        None => ColorConfig::Auto,
 
         Some(arg) => {
-            early_error(Auto, &format!("argument for --color must be auto, always \
-                                        or never (instead was `{}`)",
-                                       arg))
+            early_error(ErrorOutputType::default(), &format!("argument for --color must be auto, \
+                                                              always or never (instead was `{}`)",
+                                                            arg))
         }
     };
 
+    // We need the opts_present check because the driver will send us Matches
+    // with only stable options if no unstable options are used. Since error-format
+    // is unstable, it will not be present. We have to use opts_present not
+    // opt_present because the latter will panic.
+    let error_format = if matches.opts_present(&["error-format".to_owned()]) {
+        match matches.opt_str("error-format").as_ref().map(|s| &s[..]) {
+            Some("human")   => ErrorOutputType::HumanReadable(color),
+            Some("json") => ErrorOutputType::Json,
+
+            None => ErrorOutputType::HumanReadable(color),
+
+            Some(arg) => {
+                early_error(ErrorOutputType::HumanReadable(color),
+                            &format!("argument for --error-format must be human or json (instead \
+                                      was `{}`)",
+                                     arg))
+            }
+        }
+    } else {
+        ErrorOutputType::HumanReadable(color)
+    };
+
     let unparsed_crate_types = matches.opt_strs("crate-type");
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
-        .unwrap_or_else(|e| early_error(color, &e[..]));
+        .unwrap_or_else(|e| early_error(error_format, &e[..]));
 
     let mut lint_opts = vec!();
     let mut describe_lints = false;
@@ -916,23 +1320,24 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let lint_cap = matches.opt_str("cap-lints").map(|cap| {
         lint::Level::from_str(&cap).unwrap_or_else(|| {
-            early_error(color, &format!("unknown lint level: `{}`", cap))
+            early_error(error_format, &format!("unknown lint level: `{}`", cap))
         })
     });
 
-    let debugging_opts = build_debugging_options(matches, color);
+    let mut debugging_opts = build_debugging_options(matches, error_format);
 
-    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 no_analysis = debugging_opts.no_analysis;
-
-    if debugging_opts.debug_llvm {
-        unsafe { llvm::LLVMSetDebug(1); }
+    // Incremental compilation only works reliably when translation is done via
+    // MIR, so let's enable -Z orbit if necessary (see #34973).
+    if debugging_opts.incremental.is_some() && !debugging_opts.orbit {
+        early_warn(error_format, "Automatically enabling `-Z orbit` because \
+                                  `-Z incremental` was specified");
+        debugging_opts.orbit = true;
     }
 
-    let mut output_types = HashMap::new();
-    if !debugging_opts.parse_only && !no_trans {
+    let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1);
+
+    let mut output_types = BTreeMap::new();
+    if !debugging_opts.parse_only {
         for list in matches.opt_strs("emit") {
             for output_type in list.split(',') {
                 let mut parts = output_type.splitn(2, '=');
@@ -944,7 +1349,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
                     "link" => OutputType::Exe,
                     "dep-info" => OutputType::DepInfo,
                     part => {
-                        early_error(color, &format!("unknown emission type: `{}`",
+                        early_error(error_format, &format!("unknown emission type: `{}`",
                                                     part))
                     }
                 };
@@ -957,7 +1362,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         output_types.insert(OutputType::Exe, None);
     }
 
-    let mut cg = build_codegen_options(matches, color);
+    let mut cg = build_codegen_options(matches, error_format);
 
     // Issue #30063: if user requests llvm-related output to one
     // particular path, disable codegen-units.
@@ -969,15 +1374,37 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             }).collect();
         if !incompatible.is_empty() {
             for ot in &incompatible {
-                early_warn(color, &format!("--emit={} with -o incompatible with \
-                                            -C codegen-units=N for N > 1",
-                                           ot.shorthand()));
+                early_warn(error_format, &format!("--emit={} with -o incompatible with \
+                                                 -C codegen-units=N for N > 1",
+                                                ot.shorthand()));
             }
-            early_warn(color, "resetting to default -C codegen-units=1");
+            early_warn(error_format, "resetting to default -C codegen-units=1");
             cg.codegen_units = 1;
         }
     }
 
+    if cg.codegen_units < 1 {
+        early_error(error_format, "Value for codegen units must be a positive nonzero integer");
+    }
+
+    let mut prints = Vec::<PrintRequest>::new();
+    if cg.target_cpu.as_ref().map_or(false, |s| s == "help") {
+        prints.push(PrintRequest::TargetCPUs);
+        cg.target_cpu = None;
+    };
+    if cg.target_feature == "help" {
+        prints.push(PrintRequest::TargetFeatures);
+        cg.target_feature = "".to_string();
+    }
+    if cg.relocation_model.as_ref().map_or(false, |s| s == "help") {
+        prints.push(PrintRequest::RelocationModels);
+        cg.relocation_model = None;
+    }
+    if cg.code_model.as_ref().map_or(false, |s| s == "help") {
+        prints.push(PrintRequest::CodeModels);
+        cg.code_model = None;
+    }
+
     let cg = cg;
 
     let sysroot_opt = matches.opt_str("sysroot").map(|m| PathBuf::from(&m));
@@ -986,29 +1413,35 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let opt_level = {
         if matches.opt_present("O") {
             if cg.opt_level.is_some() {
-                early_error(color, "-O and -C opt-level both provided");
+                early_error(error_format, "-O and -C opt-level both provided");
             }
-            Default
+            OptLevel::Default
         } else {
-            match cg.opt_level {
-                None => No,
-                Some(0) => No,
-                Some(1) => Less,
-                Some(2) => Default,
-                Some(3) => Aggressive,
-                Some(arg) => {
-                    early_error(color, &format!("optimization level needs to be \
-                                                 between 0-3 (instead was `{}`)",
-                                                arg));
+            match (cg.opt_level.as_ref().map(String::as_ref),
+                   nightly_options::is_nightly_build()) {
+                (None, _) => OptLevel::No,
+                (Some("0"), _) => OptLevel::No,
+                (Some("1"), _) => OptLevel::Less,
+                (Some("2"), _) => OptLevel::Default,
+                (Some("3"), _) => OptLevel::Aggressive,
+                (Some("s"), true) => OptLevel::Size,
+                (Some("z"), true) => OptLevel::SizeMin,
+                (Some("s"), false) | (Some("z"), false) => {
+                    early_error(error_format, &format!("the optimizations s or z are only \
+                                                        accepted on the nightly compiler"));
+                },
+                (Some(arg), _) => {
+                    early_error(error_format, &format!("optimization level needs to be \
+                                                      between 0-3 (instead was `{}`)",
+                                                     arg));
                 }
             }
         }
     };
-    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == No);
-    let gc = debugging_opts.gc;
+    let debug_assertions = cg.debug_assertions.unwrap_or(opt_level == OptLevel::No);
     let debuginfo = if matches.opt_present("g") {
         if cg.debuginfo.is_some() {
-            early_error(color, "-g and -C debuginfo both provided");
+            early_error(error_format, "-g and -C debuginfo both provided");
         }
         FullDebugInfo
     } else {
@@ -1017,16 +1450,16 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             Some(1) => LimitedDebugInfo,
             Some(2) => FullDebugInfo,
             Some(arg) => {
-                early_error(color, &format!("debug info level needs to be between \
-                                             0-2 (instead was `{}`)",
-                                            arg));
+                early_error(error_format, &format!("debug info level needs to be between \
+                                                  0-2 (instead was `{}`)",
+                                                 arg));
             }
         }
     };
 
     let mut search_paths = SearchPaths::new();
     for s in &matches.opt_strs("L") {
-        search_paths.add_path(&s[..], color);
+        search_paths.add_path(&s[..], error_format);
     }
 
     let libs = matches.opt_strs("l").into_iter().map(|s| {
@@ -1038,9 +1471,9 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
             (Some(name), "framework") => (name, cstore::NativeFramework),
             (Some(name), "static") => (name, cstore::NativeStatic),
             (_, s) => {
-                early_error(color, &format!("unknown library kind `{}`, expected \
-                                             one of dylib, framework, or static",
-                                            s));
+                early_error(error_format, &format!("unknown library kind `{}`, expected \
+                                                  one of dylib, framework, or static",
+                                                 s));
             }
         };
         (name.to_string(), kind)
@@ -1049,69 +1482,75 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
     let cfg = parse_cfgspecs(matches.opt_strs("cfg"));
     let test = matches.opt_present("test");
 
-    let prints = matches.opt_strs("print").into_iter().map(|s| {
+    prints.extend(matches.opt_strs("print").into_iter().map(|s| {
         match &*s {
             "crate-name" => PrintRequest::CrateName,
             "file-names" => PrintRequest::FileNames,
             "sysroot" => PrintRequest::Sysroot,
+            "cfg" => PrintRequest::Cfg,
+            "target-list" => PrintRequest::TargetList,
+            "target-cpus" => PrintRequest::TargetCPUs,
+            "target-features" => PrintRequest::TargetFeatures,
+            "relocation-models" => PrintRequest::RelocationModels,
+            "code-models" => PrintRequest::CodeModels,
             req => {
-                early_error(color, &format!("unknown print request `{}`", req))
+                early_error(error_format, &format!("unknown print request `{}`", req))
             }
         }
-    }).collect::<Vec<_>>();
+    }));
 
     if !cg.remark.is_empty() && debuginfo == NoDebugInfo {
-        early_warn(color, "-C remark will not show source locations without \
-                           --debuginfo");
+        early_warn(error_format, "-C remark will not show source locations without \
+                                --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() {
             Some(s) => s,
-            None => early_error(color, "--extern value must not be empty"),
+            None => early_error(error_format, "--extern value must not be empty"),
         };
         let location = match parts.next() {
             Some(s) => s,
-            None => early_error(color, "--extern value must be of the format `foo=bar`"),
+            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");
 
-    Options {
+    let incremental = debugging_opts.incremental.as_ref().map(|m| PathBuf::from(m));
+
+    (Options {
         crate_types: crate_types,
-        gc: gc,
         optimize: opt_level,
         debuginfo: debuginfo,
         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,
-        no_analysis: no_analysis,
+        mir_opt_level: mir_opt_level,
+        incremental: incremental,
         debugging_opts: debugging_opts,
         prints: prints,
         cg: cg,
-        color: color,
-        show_span: None,
-        externs: externs,
+        error_format: error_format,
+        externs: Externs(externs),
         crate_name: crate_name,
         alt_std_name: None,
         libs: libs,
         unstable_features: get_unstable_features_setting(),
         debug_assertions: debug_assertions,
-    }
+    },
+    cfg)
 }
 
 pub fn get_unstable_features_setting() -> UnstableFeatures {
@@ -1130,7 +1569,6 @@ pub fn get_unstable_features_setting() -> UnstableFeatures {
 }
 
 pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateType>, String> {
-
     let mut crate_types: Vec<CrateType> = Vec::new();
     for unparsed_crate_type in &list_list {
         for part in unparsed_crate_type.split(',') {
@@ -1139,6 +1577,7 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
                 "rlib"      => CrateTypeRlib,
                 "staticlib" => CrateTypeStaticlib,
                 "dylib"     => CrateTypeDylib,
+                "cdylib"    => CrateTypeCdylib,
                 "bin"       => CrateTypeExecutable,
                 _ => {
                     return Err(format!("unknown crate type: `{}`",
@@ -1154,41 +1593,247 @@ pub fn parse_crate_types_from_list(list_list: Vec<String>) -> Result<Vec<CrateTy
     return Ok(crate_types);
 }
 
+pub mod nightly_options {
+    use getopts;
+    use syntax::feature_gate::UnstableFeatures;
+    use super::{ErrorOutputType, OptionStability, RustcOptGroup, get_unstable_features_setting};
+    use session::{early_error, early_warn};
+
+    pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
+        is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
+    }
+
+    pub fn is_nightly_build() -> bool {
+        match get_unstable_features_setting() {
+            UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
+            _ => false,
+        }
+    }
+
+    pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
+        let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
+        let really_allows_unstable_options = match get_unstable_features_setting() {
+            UnstableFeatures::Disallow => false,
+            _ => true,
+        };
+
+        for opt in flags.iter() {
+            if opt.stability == OptionStability::Stable {
+                continue
+            }
+            let opt_name = if opt.opt_group.long_name.is_empty() {
+                &opt.opt_group.short_name
+            } else {
+                &opt.opt_group.long_name
+            };
+            if !matches.opt_present(opt_name) {
+                continue
+            }
+            if opt_name != "Z" && !has_z_unstable_option {
+                early_error(ErrorOutputType::default(),
+                            &format!("the `-Z unstable-options` flag must also be passed to enable \
+                                      the flag `{}`",
+                                     opt_name));
+            }
+            if really_allows_unstable_options {
+                continue
+            }
+            match opt.stability {
+                OptionStability::Unstable => {
+                    let msg = format!("the option `{}` is only accepted on the \
+                                       nightly compiler", opt_name);
+                    early_error(ErrorOutputType::default(), &msg);
+                }
+                OptionStability::UnstableButNotReally => {
+                    let msg = format!("the option `{}` is unstable and should \
+                                       only be used on the nightly compiler, but \
+                                       it is currently accepted for backwards \
+                                       compatibility; this will soon change, \
+                                       see issue #31847 for more details",
+                                      opt_name);
+                    early_warn(ErrorOutputType::default(), &msg);
+                }
+                OptionStability::Stable => {}
+            }
+        }
+    }
+}
+
 impl fmt::Display for CrateType {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             CrateTypeExecutable => "bin".fmt(f),
             CrateTypeDylib => "dylib".fmt(f),
             CrateTypeRlib => "rlib".fmt(f),
-            CrateTypeStaticlib => "staticlib".fmt(f)
+            CrateTypeStaticlib => "staticlib".fmt(f),
+            CrateTypeCdylib => "cdylib".fmt(f),
+        }
+    }
+}
+
+/// 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::feature_gate::UnstableFeatures;
+
+    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: Vec<&$t> = self.iter().collect();
+                    elems.sort();
+                    Hash::hash(&elems.len(), hasher);
+                    for (index, elem) in elems.iter().enumerate() {
+                        Hash::hash(&index, hasher);
+                        DepTrackingHash::hash(*elem, 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<bool>);
+    impl_dep_tracking_hash_via_hash!(Option<usize>);
+    impl_dep_tracking_hash_via_hash!(Option<String>);
+    impl_dep_tracking_hash_via_hash!(Option<lint::Level>);
+    impl_dep_tracking_hash_via_hash!(Option<PathBuf>);
+    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);
+        }
+    }
+
+    impl<T1, T2> 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 middle::cstore::DummyCrateStore;
-    use session::config::{build_configuration, optgroups, build_session_options};
+    use dep_graph::DepGraph;
+    use errors;
+    use getopts::{getopts, OptGroup};
+    use lint;
+    use middle::cstore::{self, DummyCrateStore};
+    use session::config::{build_configuration, build_session_options_and_crate_config};
     use session::build_session;
-
+    use std::collections::{BTreeMap, BTreeSet};
+    use std::iter::FromIterator;
+    use std::path::PathBuf;
     use std::rc::Rc;
-    use getopts::getopts;
+    use super::{OutputType, OutputTypes, Externs, PanicStrategy};
     use syntax::attr;
     use syntax::attr::AttrMetaMethods;
-    use syntax::diagnostics;
+
+    fn optgroups() -> Vec<OptGroup> {
+        super::rustc_optgroups().into_iter()
+                                .map(|a| a.opt_group)
+                                .collect()
+    }
+
+    fn mk_map<K: Ord, V>(entries: Vec<(K, V)>) -> BTreeMap<K, V> {
+        BTreeMap::from_iter(entries.into_iter())
+    }
+
+    fn mk_set<V: Ord>(entries: Vec<V>) -> BTreeSet<V> {
+        BTreeSet::from_iter(entries.into_iter())
+    }
 
     // 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 getopts(&["--test".to_string()], &optgroups()) {
               Ok(m) => m,
               Err(f) => panic!("test_switch_implies_cfg_test: {}", f)
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
-        let sessopts = build_session_options(matches);
-        let sess = build_session(sessopts, None, registry, Rc::new(DummyCrateStore));
-        let cfg = build_configuration(&sess);
+        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, Rc::new(DummyCrateStore));
+        let cfg = build_configuration(&sess, cfg);
         assert!((attr::contains_name(&cfg[..], "test")));
     }
 
@@ -1196,6 +1841,7 @@ mod tests {
     // another --cfg test
     #[test]
     fn test_switch_implies_cfg_test_unless_cfg_test() {
+        let dep_graph = DepGraph::new(false);
         let matches =
             &match getopts(&["--test".to_string(), "--cfg=test".to_string()],
                            &optgroups()) {
@@ -1204,11 +1850,11 @@ mod tests {
                 panic!("test_switch_implies_cfg_test_unless_cfg_test: {}", f)
               }
             };
-        let registry = diagnostics::registry::Registry::new(&[]);
-        let sessopts = build_session_options(matches);
-        let sess = build_session(sessopts, None, registry,
+        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,
                                  Rc::new(DummyCrateStore));
-        let cfg = build_configuration(&sess);
+        let cfg = build_configuration(&sess, cfg);
         let mut test_items = cfg.iter().filter(|m| m.name() == "test");
         assert!(test_items.next().is_some());
         assert!(test_items.next().is_none());
@@ -1216,15 +1862,16 @@ mod tests {
 
     #[test]
     fn test_can_print_warnings() {
+        let dep_graph = DepGraph::new(false);
         {
             let matches = getopts(&[
                 "-Awarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
-            let sess = build_session(sessopts, None, registry,
+            let registry = errors::registry::Registry::new(&[]);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
+            let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(!sess.can_print_warnings);
+            assert!(!sess.diagnostic().can_emit_warnings);
         }
 
         {
@@ -1232,22 +1879,601 @@ mod tests {
                 "-Awarnings".to_string(),
                 "-Dwarnings".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
-            let sess = build_session(sessopts, None, registry,
+            let registry = errors::registry::Registry::new(&[]);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
+            let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(sess.can_print_warnings);
+            assert!(sess.diagnostic().can_emit_warnings);
         }
 
         {
             let matches = getopts(&[
                 "-Adead_code".to_string()
             ], &optgroups()).unwrap();
-            let registry = diagnostics::registry::Registry::new(&[]);
-            let sessopts = build_session_options(&matches);
-            let sess = build_session(sessopts, None, registry,
+            let registry = errors::registry::Registry::new(&[]);
+            let (sessopts, _) = build_session_options_and_crate_config(&matches);
+            let sess = build_session(sessopts, &dep_graph, None, registry,
                                      Rc::new(DummyCrateStore));
-            assert!(sess.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_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());
+    }
 }