]> git.lizzy.rs Git - rust.git/blobdiff - src/bin/miri.rs
rustup for more LocalDefId changes
[rust.git] / src / bin / miri.rs
index 0f1501d5913b20032a73cea78d9a374881ad1467..06101fe24e2d8256d541790b71599ab1c2542099 100644 (file)
 #![feature(rustc_private)]
 
-extern crate env_logger;
-extern crate getopts;
-#[macro_use]
-extern crate log;
-extern crate log_settings;
-extern crate miri;
-extern crate rustc;
-extern crate rustc_metadata;
+extern crate rustc_middle;
 extern crate rustc_driver;
-extern crate rustc_errors;
-extern crate rustc_codegen_utils;
-extern crate syntax;
+extern crate rustc_hir;
+extern crate rustc_interface;
+extern crate rustc_session;
 
-use std::path::PathBuf;
-use std::str::FromStr;
+use std::convert::TryFrom;
 use std::env;
+use std::str::FromStr;
+
+use hex::FromHexError;
+use log::debug;
 
-use miri::MiriConfig;
-use rustc::session::Session;
-use rustc_metadata::cstore::CStore;
-use rustc_driver::{Compilation, CompilerCalls, RustcDefaultCalls};
-use rustc_driver::driver::{CompileState, CompileController};
-use rustc::session::config::{self, Input, ErrorOutputType};
-use rustc_codegen_utils::codegen_backend::CodegenBackend;
-use rustc::hir::def_id::LOCAL_CRATE;
-use syntax::ast;
+use rustc_session::CtfeBacktrace;
+use rustc_driver::Compilation;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_interface::{interface, Queries};
+use rustc_middle::ty::TyCtxt;
 
 struct MiriCompilerCalls {
-    default: Box<RustcDefaultCalls>,
-    miri_config: MiriConfig,
+    miri_config: miri::MiriConfig,
 }
 
-impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
-    fn early_callback(
+impl rustc_driver::Callbacks for MiriCompilerCalls {
+    fn after_analysis<'tcx>(
         &mut self,
-        matches: &getopts::Matches,
-        sopts: &config::Options,
-        cfg: &ast::CrateConfig,
-        descriptions: &rustc_errors::registry::Registry,
-        output: ErrorOutputType,
+        compiler: &interface::Compiler,
+        queries: &'tcx Queries<'tcx>,
     ) -> Compilation {
-        self.default.early_callback(
-            matches,
-            sopts,
-            cfg,
-            descriptions,
-            output,
-        )
-    }
-    fn no_input(
-        &mut self,
-        matches: &getopts::Matches,
-        sopts: &config::Options,
-        cfg: &ast::CrateConfig,
-        odir: &Option<PathBuf>,
-        ofile: &Option<PathBuf>,
-        descriptions: &rustc_errors::registry::Registry,
-    ) -> Option<(Input, Option<PathBuf>)> {
-        self.default.no_input(
-            matches,
-            sopts,
-            cfg,
-            odir,
-            ofile,
-            descriptions,
-        )
-    }
-    fn late_callback(
-        &mut self,
-        codegen_backend: &CodegenBackend,
-        matches: &getopts::Matches,
-        sess: &Session,
-        cstore: &CStore,
-        input: &Input,
-        odir: &Option<PathBuf>,
-        ofile: &Option<PathBuf>,
-    ) -> Compilation {
-        // Called *before* `build_controller`. Add filename to `miri` arguments.
-        self.miri_config.args.insert(0, input.filestem().to_string());
-        self.default.late_callback(codegen_backend, matches, sess, cstore, input, odir, ofile)
-    }
-    fn build_controller(
-        self: Box<Self>,
-        sess: &Session,
-        matches: &getopts::Matches,
-    ) -> CompileController<'a> {
-        let this = *self;
-        let mut control = this.default.build_controller(sess, matches);
-        control.after_hir_lowering.callback = Box::new(after_hir_lowering);
-        let miri_config = this.miri_config;
-        control.after_analysis.callback =
-            Box::new(move |state| after_analysis(state, miri_config.clone()));
-        control.after_analysis.stop = Compilation::Stop;
-        control
-    }
-}
-
-fn after_hir_lowering(state: &mut CompileState) {
-    let attr = (
-        String::from("miri"),
-        syntax::feature_gate::AttributeType::Whitelisted,
-    );
-    state.session.plugin_attributes.borrow_mut().push(attr);
-}
+        compiler.session().abort_if_errors();
 
-fn after_analysis<'a, 'tcx>(
-    state: &mut CompileState<'a, 'tcx>,
-    miri_config: MiriConfig,
-) {
-    init_late_loggers();
-    state.session.abort_if_errors();
+        queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
+            init_late_loggers(tcx);
+            let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
+            let mut config = self.miri_config.clone();
 
-    let tcx = state.tcx.unwrap();
+            // Add filename to `miri` arguments.
+            config.args.insert(0, compiler.input().filestem().to_string());
 
+            if let Some(return_code) = miri::eval_main(tcx, entry_def_id.to_def_id(), config) {
+                std::process::exit(
+                    i32::try_from(return_code).expect("Return value was too large!"),
+                );
+            }
+        });
 
-    let (entry_def_id, _) = tcx.entry_fn(LOCAL_CRATE).expect("no main function found!");
-
-    miri::eval_main(tcx, entry_def_id, miri_config);
+        compiler.session().abort_if_errors();
 
-    state.session.abort_if_errors();
+        Compilation::Stop
+    }
 }
 
 fn init_early_loggers() {
@@ -131,59 +61,65 @@ fn init_early_loggers() {
     // If it is not set, we avoid initializing now so that we can initialize
     // later with our custom settings, and *not* log anything for what happens before
     // `miri` gets started.
-    if env::var("RUST_LOG").is_ok() {
+    if env::var("RUSTC_LOG").is_ok() {
         rustc_driver::init_rustc_env_logger();
     }
 }
 
-fn init_late_loggers() {
-    // We initialize loggers right before we start evaluation. We overwrite the `RUST_LOG`
+fn init_late_loggers(tcx: TyCtxt<'_>) {
+    // We initialize loggers right before we start evaluation. We overwrite the `RUSTC_LOG`
     // env var if it is not set, control it based on `MIRI_LOG`.
     if let Ok(var) = env::var("MIRI_LOG") {
-        if env::var("RUST_LOG").is_err() {
+        if env::var("RUSTC_LOG").is_err() {
             // We try to be a bit clever here: if `MIRI_LOG` is just a single level
             // used for everything, we only apply it to the parts of rustc that are
-            // CTFE-related. Otherwise, we use it verbatim for `RUST_LOG`.
+            // CTFE-related. Otherwise, we use it verbatim for `RUSTC_LOG`.
             // This way, if you set `MIRI_LOG=trace`, you get only the right parts of
             // rustc traced, but you can also do `MIRI_LOG=miri=trace,rustc_mir::interpret=debug`.
             if log::Level::from_str(&var).is_ok() {
-                env::set_var("RUST_LOG",
-                    &format!("rustc::mir::interpret={0},rustc_mir::interpret={0}", var));
+                env::set_var(
+                    "RUSTC_LOG",
+                    &format!("rustc_middle::mir::interpret={0},rustc_mir::interpret={0}", var),
+                );
             } else {
-                env::set_var("RUST_LOG", &var);
+                env::set_var("RUSTC_LOG", &var);
             }
             rustc_driver::init_rustc_env_logger();
         }
     }
 
-    // If `MIRI_BACKTRACE` is set and `RUST_CTFE_BACKTRACE` is not, set `RUST_CTFE_BACKTRACE`.
-    // Do this late, so we really only apply this to miri's errors.
-    if let Ok(var) = env::var("MIRI_BACKTRACE") {
-        if env::var("RUST_CTFE_BACKTRACE") == Err(env::VarError::NotPresent) {
-            env::set_var("RUST_CTFE_BACKTRACE", &var);
-        }
+    // If `MIRI_BACKTRACE` is set and `RUSTC_CTFE_BACKTRACE` is not, set `RUSTC_CTFE_BACKTRACE`.
+    // Do this late, so we ideally only apply this to Miri's errors.
+    if let Ok(val) = env::var("MIRI_BACKTRACE") {
+        let ctfe_backtrace = match &*val {
+            "immediate" => CtfeBacktrace::Immediate,
+            "0" => CtfeBacktrace::Disabled,
+            _ => CtfeBacktrace::Capture,
+        };
+        *tcx.sess.ctfe_backtrace.borrow_mut() = ctfe_backtrace;
     }
 }
 
-fn find_sysroot() -> String {
-    if let Ok(sysroot) = std::env::var("MIRI_SYSROOT") {
-        return sysroot;
+/// Returns the "default sysroot" that Miri will use if no `--sysroot` flag is set.
+/// Should be a compile-time constant.
+fn compile_time_sysroot() -> Option<String> {
+    if option_env!("RUSTC_STAGE").is_some() {
+        // This is being built as part of rustc, and gets shipped with rustup.
+        // We can rely on the sysroot computation in librustc.
+        return None;
     }
-
+    // For builds outside rustc, we need to ensure that we got a sysroot
+    // that gets used as a default.  The sysroot computation in librustc would
+    // end up somewhere in the build dir.
     // Taken from PR <https://github.com/Manishearth/rust-clippy/pull/911>.
     let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
     let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
-    match (home, toolchain) {
+    Some(match (home, toolchain) {
         (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
-        _ => {
-            option_env!("RUST_SYSROOT")
-                .expect(
-                    "could not find sysroot. either set `MIRI_SYSROOT` at run-time, or at \
-                     build-time specify `RUST_SYSROOT` env var or use rustup or multirust",
-                )
-                .to_owned()
-        }
-    }
+        _ => option_env!("RUST_SYSROOT")
+            .expect("To build Miri without rustup, set the `RUST_SYSROOT` env var at build time")
+            .to_owned(),
+    })
 }
 
 fn main() {
@@ -191,25 +127,98 @@ fn main() {
 
     // Parse our arguments and split them across `rustc` and `miri`.
     let mut validate = true;
+    let mut stacked_borrows = true;
+    let mut check_alignment = true;
+    let mut communicate = false;
+    let mut ignore_leaks = false;
+    let mut seed: Option<u64> = None;
+    let mut tracked_pointer_tag: Option<miri::PtrId> = None;
+    let mut tracked_alloc_id: Option<miri::AllocId> = None;
     let mut rustc_args = vec![];
     let mut miri_args = vec![];
     let mut after_dashdash = false;
+    let mut excluded_env_vars = vec![];
     for arg in std::env::args() {
         if rustc_args.is_empty() {
             // Very first arg: for `rustc`.
             rustc_args.push(arg);
-        }
-        else if after_dashdash {
+        } else if after_dashdash {
             // Everything that comes after are `miri` args.
             miri_args.push(arg);
         } else {
             match arg.as_str() {
                 "-Zmiri-disable-validation" => {
                     validate = false;
-                },
+                }
+                "-Zmiri-disable-stacked-borrows" => {
+                    stacked_borrows = false;
+                }
+                "-Zmiri-disable-alignment-check" => {
+                    check_alignment = false;
+                }
+                "-Zmiri-disable-isolation" => {
+                    communicate = true;
+                }
+                "-Zmiri-ignore-leaks" => {
+                    ignore_leaks = true;
+                }
                 "--" => {
                     after_dashdash = true;
                 }
+                arg if arg.starts_with("-Zmiri-seed=") => {
+                    if seed.is_some() {
+                        panic!("Cannot specify -Zmiri-seed multiple times!");
+                    }
+                    let seed_raw = hex::decode(arg.trim_start_matches("-Zmiri-seed="))
+                        .unwrap_or_else(|err| match err {
+                            FromHexError::InvalidHexCharacter { .. } => panic!(
+                                "-Zmiri-seed should only contain valid hex digits [0-9a-fA-F]"
+                            ),
+                            FromHexError::OddLength =>
+                                panic!("-Zmiri-seed should have an even number of digits"),
+                            err => panic!("Unknown error decoding -Zmiri-seed as hex: {:?}", err),
+                        });
+                    if seed_raw.len() > 8 {
+                        panic!(format!(
+                            "-Zmiri-seed must be at most 8 bytes, was {}",
+                            seed_raw.len()
+                        ));
+                    }
+
+                    let mut bytes = [0; 8];
+                    bytes[..seed_raw.len()].copy_from_slice(&seed_raw);
+                    seed = Some(u64::from_be_bytes(bytes));
+                }
+                arg if arg.starts_with("-Zmiri-env-exclude=") => {
+                    excluded_env_vars
+                        .push(arg.trim_start_matches("-Zmiri-env-exclude=").to_owned());
+                }
+                arg if arg.starts_with("-Zmiri-track-pointer-tag=") => {
+                    let id: u64 = match arg.trim_start_matches("-Zmiri-track-pointer-tag=").parse()
+                    {
+                        Ok(id) => id,
+                        Err(err) => panic!(
+                            "-Zmiri-track-pointer-tag requires a valid `u64` as the argument: {}",
+                            err
+                        ),
+                    };
+                    if let Some(id) = miri::PtrId::new(id) {
+                        tracked_pointer_tag = Some(id);
+                    } else {
+                        panic!("-Zmiri-track-pointer-tag must be a nonzero id");
+                    }
+                }
+                arg if arg.starts_with("-Zmiri-track-alloc-id=") => {
+                    let id: u64 = match arg.trim_start_matches("-Zmiri-track-alloc-id=").parse()
+                    {
+                        Ok(id) => id,
+                        Err(err) => panic!(
+                            "-Zmiri-track-alloc-id requires a valid `u64` as the argument: {}",
+                            err
+                        ),
+                    };
+                    tracked_alloc_id = Some(miri::AllocId(id));
+                }
                 _ => {
                     rustc_args.push(arg);
                 }
@@ -217,23 +226,44 @@ fn main() {
         }
     }
 
-    // Determine sysroot and let rustc know about it.
-    let sysroot_flag = String::from("--sysroot");
-    if !rustc_args.contains(&sysroot_flag) {
-        rustc_args.push(sysroot_flag);
-        rustc_args.push(find_sysroot());
+    // Determine sysroot if needed.  Make sure we always call `compile_time_sysroot`
+    // as that also does some sanity-checks of the environment we were built in.
+    // FIXME: Ideally we'd turn a bad build env into a compile-time error, but
+    // CTFE does not seem powerful enough for that yet.
+    if let Some(sysroot) = compile_time_sysroot() {
+        let sysroot_flag = "--sysroot";
+        if !rustc_args.iter().any(|e| e == sysroot_flag) {
+            // We need to overwrite the default that librustc would compute.
+            rustc_args.push(sysroot_flag.to_owned());
+            rustc_args.push(sysroot);
+        }
     }
+
     // Finally, add the default flags all the way in the beginning, but after the binary name.
     rustc_args.splice(1..1, miri::miri_default_args().iter().map(ToString::to_string));
 
     debug!("rustc arguments: {:?}", rustc_args);
     debug!("miri arguments: {:?}", miri_args);
-    let miri_config = MiriConfig { validate, args: miri_args };
-    let result = rustc_driver::run(move || {
-        rustc_driver::run_compiler(&rustc_args, Box::new(MiriCompilerCalls {
-            default: Box::new(RustcDefaultCalls),
-            miri_config,
-        }), None, None)
-    });
-    std::process::exit(result as i32);
+    let miri_config = miri::MiriConfig {
+        validate,
+        stacked_borrows,
+        check_alignment,
+        communicate,
+        ignore_leaks,
+        excluded_env_vars,
+        seed,
+        args: miri_args,
+        tracked_pointer_tag,
+        tracked_alloc_id,
+    };
+    rustc_driver::install_ice_hook();
+    let result = rustc_driver::catch_fatal_errors(move || {
+        rustc_driver::run_compiler(&rustc_args, &mut MiriCompilerCalls { miri_config }, None, None)
+    })
+    .and_then(|result| result);
+    let exit_code = match result {
+        Ok(()) => rustc_driver::EXIT_SUCCESS,
+        Err(_) => rustc_driver::EXIT_FAILURE,
+    };
+    std::process::exit(exit_code);
 }