Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

when terminating during unwinding, show the reason why #115045

Merged
merged 8 commits into from
Aug 25, 2023
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/invalidation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
}
}
TerminatorKind::Goto { target: _ }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable
| TerminatorKind::FalseEdge { real_target: _, imaginary_target: _ }
| TerminatorKind::FalseUnwind { real_target: _, unwind: _ } => {
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}

TerminatorKind::Goto { target: _ }
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable
| TerminatorKind::UnwindResume
| TerminatorKind::Return
Expand Down Expand Up @@ -817,7 +817,7 @@ impl<'cx, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx, R> for MirBorro
}
}

TerminatorKind::UnwindTerminate
TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Assert { .. }
| TerminatorKind::Call { .. }
| TerminatorKind::Drop { .. }
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1334,7 +1334,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
match &term.kind {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
Expand Down Expand Up @@ -1613,7 +1613,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, block_data, "resume on non-cleanup block!")
}
}
TerminatorKind::UnwindTerminate => {
TerminatorKind::UnwindTerminate(_) => {
if !is_cleanup {
span_mirbug!(self, block_data, "abort on non-cleanup block!")
}
Expand Down Expand Up @@ -1697,7 +1697,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
span_mirbug!(self, ctxt, "unwind on cleanup block")
}
}
UnwindAction::Unreachable | UnwindAction::Terminate => (),
UnwindAction::Unreachable | UnwindAction::Terminate(_) => (),
}
}

Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_codegen_cranelift/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
*destination,
);
}
TerminatorKind::UnwindTerminate => {
codegen_panic_cannot_unwind(fx, source_info);
TerminatorKind::UnwindTerminate(reason) => {
codegen_unwind_terminate(fx, source_info, *reason);
davidtwco marked this conversation as resolved.
Show resolved Hide resolved
}
TerminatorKind::UnwindResume => {
// FIXME implement unwinding
Expand Down Expand Up @@ -971,13 +971,14 @@ pub(crate) fn codegen_panic_nounwind<'tcx>(
codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, source_info.span);
}

pub(crate) fn codegen_panic_cannot_unwind<'tcx>(
pub(crate) fn codegen_unwind_terminate<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
source_info: mir::SourceInfo,
reason: UnwindTerminateReason,
) {
let args = [];

codegen_panic_inner(fx, rustc_hir::LangItem::PanicCannotUnwind, &args, source_info.span);
codegen_panic_inner(fx, reason.lang_item(), &args, source_info.span);
}

fn codegen_panic_inner<'tcx>(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
TerminatorKind::Goto { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::Drop { .. }
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_ssa/src/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
match data.terminator().kind {
TerminatorKind::Goto { .. }
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
Expand Down
149 changes: 76 additions & 73 deletions compiler/rustc_codegen_ssa/src/mir/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::MemFlags;
use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
use rustc_middle::mir::{self, AssertKind, SwitchTargets, UnwindTerminateReason};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty};
Expand Down Expand Up @@ -178,7 +178,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
mir::UnwindAction::Terminate => {
mir::UnwindAction::Terminate(reason) => {
if fx.mir[self.bb].is_cleanup && base::wants_new_eh_instructions(fx.cx.tcx().sess) {
// MSVC SEH will abort automatically if an exception tries to
// propagate out from cleanup.
Expand All @@ -191,7 +191,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {

None
} else {
Some(fx.terminate_block())
Some(fx.terminate_block(reason))
}
}
};
Expand Down Expand Up @@ -264,7 +264,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
) -> MergingSucc {
let unwind_target = match unwind {
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
mir::UnwindAction::Terminate => Some(fx.terminate_block()),
mir::UnwindAction::Terminate(reason) => Some(fx.terminate_block(reason)),
mir::UnwindAction::Continue => None,
mir::UnwindAction::Unreachable => None,
};
Expand Down Expand Up @@ -649,12 +649,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
helper: TerminatorCodegenHelper<'tcx>,
bx: &mut Bx,
terminator: &mir::Terminator<'tcx>,
reason: UnwindTerminateReason,
) {
let span = terminator.source_info.span;
self.set_debug_loc(bx, terminator.source_info);

// Obtain the panic entry point.
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicCannotUnwind);
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), reason.lang_item());

// Codegen the actual panic invoke/call.
let merging_succ = helper.do_call(
Expand Down Expand Up @@ -1229,8 +1230,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MergingSucc::False
}

mir::TerminatorKind::UnwindTerminate => {
self.codegen_terminate_terminator(helper, bx, terminator);
mir::TerminatorKind::UnwindTerminate(reason) => {
self.codegen_terminate_terminator(helper, bx, terminator, reason);
MergingSucc::False
}

Expand Down Expand Up @@ -1579,79 +1580,81 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
}

fn terminate_block(&mut self) -> Bx::BasicBlock {
self.terminate_block.unwrap_or_else(|| {
let funclet;
let llbb;
let mut bx;
if base::wants_msvc_seh(self.cx.sess()) {
// This is a basic block that we're aborting the program for,
// notably in an `extern` function. These basic blocks are inserted
// so that we assert that `extern` functions do indeed not panic,
// and if they do we abort the process.
//
// On MSVC these are tricky though (where we're doing funclets). If
// we were to do a cleanuppad (like below) the normal functions like
// `longjmp` would trigger the abort logic, terminating the
// program. Instead we insert the equivalent of `catch(...)` for C++
// which magically doesn't trigger when `longjmp` files over this
// frame.
//
// Lots more discussion can be found on #48251 but this codegen is
// modeled after clang's for:
//
// try {
// foo();
// } catch (...) {
// bar();
// }
//
// which creates an IR snippet like
//
// cs_terminate:
// %cs = catchswitch within none [%cp_terminate] unwind to caller
// cp_terminate:
// %cp = catchpad within %cs [null, i32 64, null]
// ...

llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");

let mut cs_bx = Bx::build(self.cx, llbb);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);

// The "null" here is actually a RTTI type descriptor for the
// C++ personality function, but `catch (...)` has no type so
// it's null. The 64 here is actually a bitfield which
// represents that this is a catch-all block.
bx = Bx::build(self.cx, cp_llbb);
let null =
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
let sixty_four = bx.const_i32(64);
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);
fn terminate_block(&mut self, reason: UnwindTerminateReason) -> Bx::BasicBlock {
if let Some((cached_bb, cached_reason)) = self.terminate_block && reason == cached_reason {
return cached_bb;
}

let llpersonality = self.cx.eh_personality();
bx.filter_landing_pad(llpersonality);
let funclet;
let llbb;
let mut bx;
if base::wants_msvc_seh(self.cx.sess()) {
// This is a basic block that we're aborting the program for,
// notably in an `extern` function. These basic blocks are inserted
// so that we assert that `extern` functions do indeed not panic,
// and if they do we abort the process.
//
// On MSVC these are tricky though (where we're doing funclets). If
// we were to do a cleanuppad (like below) the normal functions like
// `longjmp` would trigger the abort logic, terminating the
// program. Instead we insert the equivalent of `catch(...)` for C++
// which magically doesn't trigger when `longjmp` files over this
// frame.
//
// Lots more discussion can be found on #48251 but this codegen is
// modeled after clang's for:
//
// try {
// foo();
// } catch (...) {
// bar();
// }
//
// which creates an IR snippet like
//
// cs_terminate:
// %cs = catchswitch within none [%cp_terminate] unwind to caller
// cp_terminate:
// %cp = catchpad within %cs [null, i32 64, null]
// ...

llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");

let mut cs_bx = Bx::build(self.cx, llbb);
let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);

// The "null" here is actually a RTTI type descriptor for the
// C++ personality function, but `catch (...)` has no type so
// it's null. The 64 here is actually a bitfield which
// represents that this is a catch-all block.
bx = Bx::build(self.cx, cp_llbb);
let null =
bx.const_null(bx.type_ptr_ext(bx.cx().data_layout().instruction_address_space));
let sixty_four = bx.const_i32(64);
funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
} else {
llbb = Bx::append_block(self.cx, self.llfn, "terminate");
bx = Bx::build(self.cx, llbb);

funclet = None;
}
let llpersonality = self.cx.eh_personality();
bx.filter_landing_pad(llpersonality);

funclet = None;
}

self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));

let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, reason.lang_item());
let fn_ty = bx.fn_decl_backend_type(&fn_abi);

let llret = bx.call(fn_ty, None, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);
let llret = bx.call(fn_ty, None, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);

bx.unreachable();
bx.unreachable();

self.terminate_block = Some(llbb);
llbb
})
self.terminate_block = Some((llbb, reason));
llbb
}

/// Get the backend `BasicBlock` for a MIR `BasicBlock`, either already
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_codegen_ssa/src/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rustc_index::IndexVec;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::mir::traversal;
use rustc_middle::mir::UnwindTerminateReason;
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
use rustc_target::abi::call::{FnAbi, PassMode};
Expand Down Expand Up @@ -83,8 +84,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// Cached unreachable block
unreachable_block: Option<Bx::BasicBlock>,

/// Cached terminate upon unwinding block
terminate_block: Option<Bx::BasicBlock>,
/// Cached terminate upon unwinding block and its reason
terminate_block: Option<(Bx::BasicBlock, UnwindTerminateReason)>,

/// The location where each MIR arg/var/tmp/ret is stored. This is
/// usually an `PlaceRef` representing an alloca, but not always:
Expand Down Expand Up @@ -174,7 +175,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let mut start_bx = Bx::build(cx, start_llbb);

if mir.basic_blocks.iter().any(|bb| {
bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate))
bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate(_)))
}) {
start_bx.set_personality_fn(cx.eh_personality());
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_const_eval/src/interpret/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,16 +756,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
///
/// If `target` is `UnwindAction::Unreachable`, that indicates the function does not allow
/// unwinding, and doing so is UB.
#[cold] // usually we have normal returns, not unwinding
pub fn unwind_to_block(&mut self, target: mir::UnwindAction) -> InterpResult<'tcx> {
self.frame_mut().loc = match target {
mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
mir::UnwindAction::Unreachable => {
throw_ub_custom!(fluent::const_eval_unreachable_unwind);
}
mir::UnwindAction::Terminate => {
mir::UnwindAction::Terminate(reason) => {
self.frame_mut().loc = Right(self.frame_mut().body.span);
M::unwind_terminate(self)?;
M::unwind_terminate(self, reason)?;
// This might have pushed a new stack frame, or it terminated execution.
// Either way, `loc` will not be updated.
return Ok(());
Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_const_eval/src/interpret/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,10 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
fn panic_nounwind(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: &str) -> InterpResult<'tcx>;

/// Called when unwinding reached a state where execution should be terminated.
fn unwind_terminate(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
fn unwind_terminate(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
reason: mir::UnwindTerminateReason,
) -> InterpResult<'tcx>;

/// Called for all binary operations where the LHS has pointer type.
///
Expand Down Expand Up @@ -462,6 +465,7 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {

/// Called immediately after a stack frame got popped, but before jumping back to the caller.
/// The `locals` have already been destroyed!
#[inline(always)]
fn after_stack_pop(
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
_frame: Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>,
Expand Down Expand Up @@ -501,7 +505,10 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
}

#[inline(always)]
fn unwind_terminate(_ecx: &mut InterpCx<$mir, $tcx, Self>) -> InterpResult<$tcx> {
fn unwind_terminate(
_ecx: &mut InterpCx<$mir, $tcx, Self>,
_reason: mir::UnwindTerminateReason,
) -> InterpResult<$tcx> {
unreachable!("unwinding cannot happen during compile-time evaluation")
}

Expand Down