diff --git a/src/Cargo.lock b/src/Cargo.lock index c22187ee13e8e..f904bd5bf43e1 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1919,6 +1919,7 @@ dependencies = [ "log_settings 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_apfloat 0.0.0", + "rustc_back 0.0.0", "rustc_const_eval 0.0.0", "rustc_const_math 0.0.0", "rustc_data_structures 0.0.0", diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index a54f15532b18f..ad64244f7c5af 100644 --- a/src/librustc_mir/Cargo.toml +++ b/src/librustc_mir/Cargo.toml @@ -14,6 +14,7 @@ graphviz = { path = "../libgraphviz" } log = "0.3" log_settings = "0.1.1" rustc = { path = "../librustc" } +rustc_back = { path = "../librustc_back" } rustc_const_eval = { path = "../librustc_const_eval" } rustc_const_math = { path = "../librustc_const_math" } rustc_data_structures = { path = "../librustc_data_structures" } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index d814b092c9d69..4b8ec062a78dd 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -20,6 +20,7 @@ use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::Substs; use rustc::util::nodemap::NodeMap; +use rustc_back::PanicStrategy; use rustc_const_eval::pattern::{BindingMode, PatternKind}; use rustc_data_structures::indexed_vec::{IndexVec, Idx}; use shim; @@ -353,6 +354,27 @@ macro_rules! unpack { }; } +fn needs_abort_block<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + fn_id: ast::NodeId, + abi: Abi) + -> bool { + + // Not callable from C, so we can safely unwind through these + if abi == Abi::Rust || abi == Abi::RustCall { return false; } + + // We never unwind, so it's not relevant to stop an unwind + if tcx.sess.panic_strategy() != PanicStrategy::Unwind { return false; } + + // We cannot add landing pads, so don't add one + if tcx.sess.no_landing_pads() { return false; } + + // This is a special case: some functions have a C abi but are meant to + // unwind anyway. Don't stop them. + if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; } + + return true; +} + /////////////////////////////////////////////////////////////////////////// /// the main entry point for building MIR for a function @@ -383,6 +405,11 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>, let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| { + + if needs_abort_block(tcx, fn_id, abi) { + builder.schedule_abort(); + } + let arg_scope_s = (arg_scope, source_info); unpack!(block = builder.in_scope(arg_scope_s, LintLevel::Inherited, block, |builder| { builder.args_and_body(block, &arguments, arg_scope, &body.value) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 630d0bf179294..ddb4bf0e36ba7 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -612,6 +612,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } } + // Schedule an abort block - this is used for some ABIs that cannot unwind + pub fn schedule_abort(&mut self) -> BasicBlock { + self.scopes[0].needs_cleanup = true; + let abortblk = self.cfg.start_new_cleanup_block(); + let source_info = self.scopes[0].source_info(self.fn_span); + self.cfg.terminate(abortblk, source_info, TerminatorKind::Abort); + self.cached_resume_block = Some(abortblk); + abortblk + } + // Scheduling drops // ================ /// Indicates that `place` should be dropped on exit from diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index a7efdaf08264a..4556a8ecc4bb9 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -49,6 +49,7 @@ extern crate rustc_errors; #[macro_use] extern crate syntax; extern crate syntax_pos; +extern crate rustc_back; extern crate rustc_const_math; extern crate rustc_const_eval; extern crate core; // for NonZero diff --git a/src/test/run-pass/abort-on-c-abi.rs b/src/test/run-pass/abort-on-c-abi.rs new file mode 100644 index 0000000000000..63fd934b0d0f4 --- /dev/null +++ b/src/test/run-pass/abort-on-c-abi.rs @@ -0,0 +1,43 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that +// we never unwind through them. + +// ignore-emscripten no processes + +use std::{env, panic}; +use std::io::prelude::*; +use std::io; +use std::process::{Command, Stdio}; + +extern "C" fn panic_in_ffi() { + panic!("Test"); +} + +fn test() { + let _ = panic::catch_unwind(|| { panic_in_ffi(); }); + // The process should have aborted by now. + io::stdout().write(b"This should never be printed.\n"); + let _ = io::stdout().flush(); +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() > 1 && args[1] == "test" { + return test(); + } + + let mut p = Command::new(&args[0]) + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .arg("test").spawn().unwrap(); + assert!(!p.wait().unwrap().success()); +}