diff --git a/src/Cargo.lock b/src/Cargo.lock index c22187ee13e..f904bd5bf43 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/ich/impls_mir.rs b/src/librustc/ich/impls_mir.rs index df67c3abbe8..7517281b0b2 100644 --- a/src/librustc/ich/impls_mir.rs +++ b/src/librustc/ich/impls_mir.rs @@ -150,6 +150,7 @@ fn hash_stable(&self, targets.hash_stable(hcx, hasher); } mir::TerminatorKind::Resume | + mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Unreachable => {} diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f410865a6cd..befab6d436b 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -637,6 +637,10 @@ pub enum TerminatorKind<'tcx> { /// continue. Emitted by build::scope::diverge_cleanup. Resume, + /// Indicates that the landing pad is finished and that the process + /// should abort. Used to prevent unwinding for foreign items. + Abort, + /// Indicates a normal return. The return place should have /// been filled in by now. This should occur at most once. Return, @@ -759,7 +763,7 @@ pub fn successors(&self) -> Cow<[BasicBlock]> { match *self { Goto { target: ref b } => slice::from_ref(b).into_cow(), SwitchInt { targets: ref b, .. } => b[..].into_cow(), - Resume | GeneratorDrop => (&[]).into_cow(), + Resume | Abort | GeneratorDrop => (&[]).into_cow(), Return => (&[]).into_cow(), Unreachable => (&[]).into_cow(), Call { destination: Some((_, t)), cleanup: Some(c), .. } => vec![t, c].into_cow(), @@ -794,7 +798,7 @@ pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> { match *self { Goto { target: ref mut b } => vec![b], SwitchInt { targets: ref mut b, .. } => b.iter_mut().collect(), - Resume | GeneratorDrop => Vec::new(), + Resume | Abort | GeneratorDrop => Vec::new(), Return => Vec::new(), Unreachable => Vec::new(), Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut c), .. } => vec![t, c], @@ -823,6 +827,7 @@ pub fn unwind_mut(&mut self) -> Option<&mut Option> { match *self { TerminatorKind::Goto { .. } | TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::GeneratorDrop | @@ -918,6 +923,7 @@ pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { Return => write!(fmt, "return"), GeneratorDrop => write!(fmt, "generator_drop"), Resume => write!(fmt, "resume"), + Abort => write!(fmt, "abort"), Yield { ref value, .. } => write!(fmt, "_1 = suspend({:?})", value), Unreachable => write!(fmt, "unreachable"), Drop { ref location, .. } => write!(fmt, "drop({:?})", location), @@ -970,7 +976,7 @@ pub fn fmt_head(&self, fmt: &mut W) -> fmt::Result { pub fn fmt_successor_labels(&self) -> Vec> { use self::TerminatorKind::*; match *self { - Return | Resume | Unreachable | GeneratorDrop => vec![], + Return | Resume | Abort | Unreachable | GeneratorDrop => vec![], Goto { .. } => vec!["".into()], SwitchInt { ref values, .. } => { values.iter() @@ -2102,6 +2108,7 @@ fn super_fold_with<'gcx: 'tcx, F: TypeFolder<'gcx, 'tcx>>(&self, folder: &mut F) }, GeneratorDrop => GeneratorDrop, Resume => Resume, + Abort => Abort, Return => Return, Unreachable => Unreachable, FalseEdges { real_target, ref imaginary_targets } => @@ -2143,6 +2150,7 @@ fn super_visit_with>(&self, visitor: &mut V) -> bool { }, Goto { .. } | Resume | + Abort | Return | GeneratorDrop | Unreachable | diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 0e6c14af1ec..2a3f7384de2 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -432,6 +432,7 @@ fn super_terminator_kind(&mut self, } TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable => { diff --git a/src/librustc_mir/Cargo.toml b/src/librustc_mir/Cargo.toml index a54f15532b1..ad64244f7c5 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/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 1e8a6792963..4f07402d602 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -552,6 +552,7 @@ fn visit_terminator_entry( }); } TerminatorKind::Goto { target: _ } + | TerminatorKind::Abort | TerminatorKind::Unreachable | TerminatorKind::FalseEdges { .. } => { // no data used, thus irrelevant to borrowck diff --git a/src/librustc_mir/borrow_check/nll/type_check/mod.rs b/src/librustc_mir/borrow_check/nll/type_check/mod.rs index 4c8a171299f..901b73c610e 100644 --- a/src/librustc_mir/borrow_check/nll/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/nll/type_check/mod.rs @@ -780,6 +780,7 @@ fn check_terminator( match term.kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume + | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable @@ -1082,6 +1083,9 @@ fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block_data: &BasicBlockData<'tcx> TerminatorKind::Resume => if !is_cleanup { span_mirbug!(self, block_data, "resume on non-cleanup block!") }, + TerminatorKind::Abort => if !is_cleanup { + span_mirbug!(self, block_data, "abort on non-cleanup block!") + }, TerminatorKind::Return => if is_cleanup { span_mirbug!(self, block_data, "return on cleanup block") }, diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 51ebb78c74e..5d420d45aac 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -20,6 +20,7 @@ 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 should_abort_on_panic<'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,10 @@ 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 should_abort_on_panic(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 630d0bf1792..ddb4bf0e36b 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -612,6 +612,16 @@ pub fn local_scope(&self) -> Option { } } + // 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/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index c39ae10371c..a2f506afa6d 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -496,6 +496,7 @@ fn terminator_effect_on_borrows(&self, } } } + mir::TerminatorKind::Abort | mir::TerminatorKind::SwitchInt {..} | mir::TerminatorKind::Drop {..} | mir::TerminatorKind::DropAndReplace {..} | diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs index 83c46e0199e..1597ba8aaa0 100644 --- a/src/librustc_mir/dataflow/mod.rs +++ b/src/librustc_mir/dataflow/mod.rs @@ -771,6 +771,7 @@ fn propagate_bits_into_graph_successors_of( match bb_data.terminator().kind { mir::TerminatorKind::Return | mir::TerminatorKind::Resume | + mir::TerminatorKind::Abort | mir::TerminatorKind::GeneratorDrop | mir::TerminatorKind::Unreachable => {} mir::TerminatorKind::Goto { ref target } | diff --git a/src/librustc_mir/dataflow/move_paths/builder.rs b/src/librustc_mir/dataflow/move_paths/builder.rs index c20beb7d8c2..f35e52497ce 100644 --- a/src/librustc_mir/dataflow/move_paths/builder.rs +++ b/src/librustc_mir/dataflow/move_paths/builder.rs @@ -334,6 +334,7 @@ fn gather_terminator(&mut self, term: &Terminator<'tcx>) { match term.kind { TerminatorKind::Goto { target: _ } | TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::GeneratorDrop | TerminatorKind::FalseEdges { .. } | TerminatorKind::Unreachable => { } diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index 3eef0578360..c8a0dbdd903 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -163,6 +163,7 @@ pub(super) fn eval_terminator( GeneratorDrop => unimplemented!(), DropAndReplace { .. } => unimplemented!(), Resume => unimplemented!(), + Abort => unimplemented!(), FalseEdges { .. } => bug!("should have been eliminated by `simplify_branches` mir pass"), Unreachable => return err!(Unreachable), } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index a7efdaf0826..4556a8ecc4b 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -49,6 +49,7 @@ #[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/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 0d9976afb9e..0b8666800a5 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -625,6 +625,7 @@ fn visit_terminator_kind(&mut self, mir::TerminatorKind::Goto { .. } | mir::TerminatorKind::SwitchInt { .. } | mir::TerminatorKind::Resume | + mir::TerminatorKind::Abort | mir::TerminatorKind::Return | mir::TerminatorKind::Unreachable | mir::TerminatorKind::Assert { .. } => {} diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index e7ce5285071..e9ba5de3cc6 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -73,6 +73,7 @@ fn visit_terminator(&mut self, TerminatorKind::DropAndReplace { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Unreachable | TerminatorKind::FalseEdges { .. } => { diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index 72163847952..43ee75d1e2b 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -806,6 +806,7 @@ fn visit_terminator_kind(&mut self, block: BasicBlock, *kind = TerminatorKind::Goto { target: tgt } } } + TerminatorKind::Abort => { } TerminatorKind::Unreachable => { } TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => { *real_target = self.update_target(*real_target); diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2b2323928ef..f0871bb188d 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -324,6 +324,7 @@ fn qualify_const(&mut self) -> (Qualif, Rc>) { TerminatorKind::SwitchInt {..} | TerminatorKind::DropAndReplace { .. } | TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } | TerminatorKind::Unreachable | diff --git a/src/librustc_mir/transform/remove_noop_landing_pads.rs b/src/librustc_mir/transform/remove_noop_landing_pads.rs index d29174d5719..23274cdedf2 100644 --- a/src/librustc_mir/transform/remove_noop_landing_pads.rs +++ b/src/librustc_mir/transform/remove_noop_landing_pads.rs @@ -76,6 +76,7 @@ fn is_nop_landing_pad(&self, bb: BasicBlock, mir: &Mir, nop_landing_pads: &BitVe TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } | TerminatorKind::Return | + TerminatorKind::Abort | TerminatorKind::Unreachable | TerminatorKind::Call { .. } | TerminatorKind::Assert { .. } | diff --git a/src/librustc_passes/mir_stats.rs b/src/librustc_passes/mir_stats.rs index 8a9936ecb8b..b379a174b23 100644 --- a/src/librustc_passes/mir_stats.rs +++ b/src/librustc_passes/mir_stats.rs @@ -113,6 +113,7 @@ fn visit_terminator_kind(&mut self, TerminatorKind::Goto { .. } => "TerminatorKind::Goto", TerminatorKind::SwitchInt { .. } => "TerminatorKind::SwitchInt", TerminatorKind::Resume => "TerminatorKind::Resume", + TerminatorKind::Abort => "TerminatorKind::Abort", TerminatorKind::Return => "TerminatorKind::Return", TerminatorKind::Unreachable => "TerminatorKind::Unreachable", TerminatorKind::Drop { .. } => "TerminatorKind::Drop", diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index cfe55aba0d3..31cbb07dbe6 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -236,6 +236,7 @@ fn discover_masters<'tcx>(result: &mut IndexVec, match data.terminator().kind { TerminatorKind::Goto { .. } | TerminatorKind::Resume | + TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::GeneratorDrop | TerminatorKind::Unreachable | diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 1cb3a66e4d8..422b8210b35 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -180,6 +180,13 @@ fn trans_terminator(&mut self, } } + mir::TerminatorKind::Abort => { + // Call core::intrinsics::abort() + let fnname = bcx.ccx.get_intrinsic(&("llvm.trap")); + bcx.call(fnname, &[], None); + bcx.unreachable(); + } + mir::TerminatorKind::Goto { target } => { funclet_br(self, bcx, target); } 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 00000000000..63fd934b0d0 --- /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()); +}