Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 5e6ed13

Browse files
committed
Add UnwindAction::Unreachable
This also makes eval machine's `StackPopUnwind` redundant so that is replaced.
1 parent daeb844 commit 5e6ed13

File tree

23 files changed

+161
-150
lines changed

23 files changed

+161
-150
lines changed

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,47 +1612,27 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
16121612
TerminatorKind::Drop { target, unwind, .. }
16131613
| TerminatorKind::Assert { target, unwind, .. } => {
16141614
self.assert_iscleanup(body, block_data, target, is_cleanup);
1615-
if let UnwindAction::Cleanup(unwind) = unwind {
1616-
if is_cleanup {
1617-
span_mirbug!(self, block_data, "unwind on cleanup block")
1618-
}
1619-
self.assert_iscleanup(body, block_data, unwind, true);
1620-
}
1615+
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
16211616
}
16221617
TerminatorKind::Call { ref target, unwind, .. } => {
16231618
if let &Some(target) = target {
16241619
self.assert_iscleanup(body, block_data, target, is_cleanup);
16251620
}
1626-
if let UnwindAction::Cleanup(cleanup) = unwind {
1627-
if is_cleanup {
1628-
span_mirbug!(self, block_data, "cleanup on cleanup block")
1629-
}
1630-
self.assert_iscleanup(body, block_data, cleanup, true);
1631-
}
1621+
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
16321622
}
16331623
TerminatorKind::FalseEdge { real_target, imaginary_target } => {
16341624
self.assert_iscleanup(body, block_data, real_target, is_cleanup);
16351625
self.assert_iscleanup(body, block_data, imaginary_target, is_cleanup);
16361626
}
16371627
TerminatorKind::FalseUnwind { real_target, unwind } => {
16381628
self.assert_iscleanup(body, block_data, real_target, is_cleanup);
1639-
if let UnwindAction::Cleanup(unwind) = unwind {
1640-
if is_cleanup {
1641-
span_mirbug!(self, block_data, "cleanup in cleanup block via false unwind");
1642-
}
1643-
self.assert_iscleanup(body, block_data, unwind, true);
1644-
}
1629+
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
16451630
}
16461631
TerminatorKind::InlineAsm { destination, unwind, .. } => {
16471632
if let Some(target) = destination {
16481633
self.assert_iscleanup(body, block_data, target, is_cleanup);
16491634
}
1650-
if let UnwindAction::Cleanup(cleanup) = unwind {
1651-
if is_cleanup {
1652-
span_mirbug!(self, block_data, "cleanup on cleanup block")
1653-
}
1654-
self.assert_iscleanup(body, block_data, cleanup, true);
1655-
}
1635+
self.assert_iscleanup_unwind(body, block_data, unwind, is_cleanup);
16561636
}
16571637
}
16581638
}
@@ -1669,6 +1649,25 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
16691649
}
16701650
}
16711651

1652+
fn assert_iscleanup_unwind(
1653+
&mut self,
1654+
body: &Body<'tcx>,
1655+
ctxt: &dyn fmt::Debug,
1656+
unwind: UnwindAction,
1657+
is_cleanup: bool,
1658+
) {
1659+
match unwind {
1660+
UnwindAction::Cleanup(unwind) => {
1661+
if is_cleanup {
1662+
span_mirbug!(self, ctxt, "unwind on cleanup block")
1663+
}
1664+
self.assert_iscleanup(body, ctxt, unwind, true);
1665+
}
1666+
UnwindAction::Continue => (),
1667+
UnwindAction::Unreachable => (),
1668+
}
1669+
}
1670+
16721671
fn check_local(&mut self, body: &Body<'tcx>, local: Local, local_decl: &LocalDecl<'tcx>) {
16731672
match body.local_kind(local) {
16741673
LocalKind::ReturnPointer | LocalKind::Arg => {

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -156,35 +156,36 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
156156
fn_ptr: Bx::Value,
157157
llargs: &[Bx::Value],
158158
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
159-
unwind: mir::UnwindAction,
159+
mut unwind: mir::UnwindAction,
160160
copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>],
161161
mergeable_succ: bool,
162162
) -> MergingSucc {
163163
// If there is a cleanup block and the function we're calling can unwind, then
164164
// do an invoke, otherwise do a call.
165165
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
166166

167-
let cleanup = match unwind {
168-
mir::UnwindAction::Cleanup(cleanup) => Some(cleanup),
167+
if !fn_abi.can_unwind {
168+
unwind = mir::UnwindAction::Unreachable;
169+
}
170+
171+
let unwind_block = match unwind {
172+
mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
173+
_ if fx.mir[self.bb].is_cleanup
174+
&& fn_abi.can_unwind
175+
&& !base::wants_msvc_seh(fx.cx.tcx().sess) =>
176+
{
177+
// Exception must not propagate out of the execution of a cleanup (doing so
178+
// can cause undefined behaviour). We insert a double unwind guard for
179+
// functions that can potentially unwind to protect against this.
180+
//
181+
// This is not necessary for SEH which does not use successive unwinding
182+
// like Itanium EH. EH frames in SEH are different from normal function
183+
// frames and SEH will abort automatically if an exception tries to
184+
// propagate out from cleanup.
185+
Some(fx.double_unwind_guard())
186+
}
169187
mir::UnwindAction::Continue => None,
170-
};
171-
let unwind_block = if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
172-
Some(self.llbb_with_cleanup(fx, cleanup))
173-
} else if fx.mir[self.bb].is_cleanup
174-
&& fn_abi.can_unwind
175-
&& !base::wants_msvc_seh(fx.cx.tcx().sess)
176-
{
177-
// Exception must not propagate out of the execution of a cleanup (doing so
178-
// can cause undefined behaviour). We insert a double unwind guard for
179-
// functions that can potentially unwind to protect against this.
180-
//
181-
// This is not necessary for SEH which does not use successive unwinding
182-
// like Itanium EH. EH frames in SEH are different from normal function
183-
// frames and SEH will abort automatically if an exception tries to
184-
// propagate out from cleanup.
185-
Some(fx.double_unwind_guard())
186-
} else {
187-
None
188+
mir::UnwindAction::Unreachable => None,
188189
};
189190

190191
if let Some(unwind_block) = unwind_block {
@@ -640,7 +641,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
640641
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicCannotUnwind);
641642

642643
// Codegen the actual panic invoke/call.
643-
let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &[], None, mir::UnwindAction::Continue, &[], false);
644+
let merging_succ = helper.do_call(
645+
self,
646+
bx,
647+
fn_abi,
648+
llfn,
649+
&[],
650+
None,
651+
mir::UnwindAction::Unreachable,
652+
&[],
653+
false,
654+
);
644655
assert_eq!(merging_succ, MergingSucc::False);
645656
}
646657

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
2323

2424
use crate::interpret::{
2525
self, compile_time_machine, AllocId, ConstAllocation, FnVal, Frame, ImmTy, InterpCx,
26-
InterpResult, OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
26+
InterpResult, OpTy, PlaceTy, Pointer, Scalar,
2727
};
2828

2929
use super::error::*;
@@ -271,7 +271,7 @@ impl<'mir, 'tcx: 'mir> CompileTimeEvalContext<'mir, 'tcx> {
271271
/* with_caller_location = */ false,
272272
dest,
273273
ret,
274-
StackPopUnwind::NotAllowed,
274+
mir::UnwindAction::Unreachable,
275275
)?;
276276
Ok(ControlFlow::Break(()))
277277
} else {
@@ -401,7 +401,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
401401
args: &[OpTy<'tcx>],
402402
dest: &PlaceTy<'tcx>,
403403
ret: Option<mir::BasicBlock>,
404-
_unwind: StackPopUnwind, // unwinding is not supported in consts
404+
_unwind: mir::UnwindAction, // unwinding is not supported in consts
405405
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
406406
debug!("find_mir_or_eval_fn: {:?}", instance);
407407

@@ -450,7 +450,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
450450
args: &[OpTy<'tcx>],
451451
dest: &PlaceTy<'tcx, Self::Provenance>,
452452
target: Option<mir::BasicBlock>,
453-
_unwind: StackPopUnwind,
453+
_unwind: mir::UnwindAction,
454454
) -> InterpResult<'tcx> {
455455
// Shared intrinsics.
456456
if ecx.emulate_intrinsic(instance, args, dest, target)? {

compiler/rustc_const_eval/src/interpret/eval_context.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -139,25 +139,14 @@ pub struct FrameInfo<'tcx> {
139139
pub lint_root: Option<hir::HirId>,
140140
}
141141

142-
/// Unwind information.
143-
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
144-
pub enum StackPopUnwind {
145-
/// The cleanup block.
146-
Cleanup(mir::BasicBlock),
147-
/// No cleanup needs to be done.
148-
Skip,
149-
/// Unwinding is not allowed (UB).
150-
NotAllowed,
151-
}
152-
153142
#[derive(Clone, Copy, Eq, PartialEq, Debug)] // Miri debug-prints these
154143
pub enum StackPopCleanup {
155144
/// Jump to the next block in the caller, or cause UB if None (that's a function
156145
/// that may never return). Also store layout of return place so
157146
/// we can validate it at that layout.
158147
/// `ret` stores the block we jump to on a normal return, while `unwind`
159148
/// stores the block used for cleanup during unwinding.
160-
Goto { ret: Option<mir::BasicBlock>, unwind: StackPopUnwind },
149+
Goto { ret: Option<mir::BasicBlock>, unwind: mir::UnwindAction },
161150
/// The root frame of the stack: nowhere else to jump to.
162151
/// `cleanup` says whether locals are deallocated. Static computation
163152
/// wants them leaked to intern what they need (and just throw away
@@ -735,16 +724,16 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
735724
/// *Unwind* to the given `target` basic block.
736725
/// Do *not* use for returning! Use `return_to_block` instead.
737726
///
738-
/// If `target` is `StackPopUnwind::Skip`, that indicates the function does not need cleanup
727+
/// If `target` is `UnwindAction::Continue`, that indicates the function does not need cleanup
739728
/// during unwinding, and we will just keep propagating that upwards.
740729
///
741-
/// If `target` is `StackPopUnwind::NotAllowed`, that indicates the function does not allow
730+
/// If `target` is `UnwindAction::Unreachable`, that indicates the function does not allow
742731
/// unwinding, and doing so is UB.
743-
pub fn unwind_to_block(&mut self, target: StackPopUnwind) -> InterpResult<'tcx> {
732+
pub fn unwind_to_block(&mut self, target: mir::UnwindAction) -> InterpResult<'tcx> {
744733
self.frame_mut().loc = match target {
745-
StackPopUnwind::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
746-
StackPopUnwind::Skip => Right(self.frame_mut().body.span),
747-
StackPopUnwind::NotAllowed => {
734+
mir::UnwindAction::Cleanup(block) => Left(mir::Location { block, statement_index: 0 }),
735+
mir::UnwindAction::Continue => Right(self.frame_mut().body.span),
736+
mir::UnwindAction::Unreachable => {
748737
throw_ub_format!("unwinding past a stack frame that does not allow unwinding")
749738
}
750739
};

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use crate::const_eval::CheckAlignment;
1818

1919
use super::{
2020
AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx,
21-
InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind,
21+
InterpResult, MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar,
2222
};
2323

2424
/// Data returned by Machine::stack_pop,
@@ -185,7 +185,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
185185
args: &[OpTy<'tcx, Self::Provenance>],
186186
destination: &PlaceTy<'tcx, Self::Provenance>,
187187
target: Option<mir::BasicBlock>,
188-
unwind: StackPopUnwind,
188+
unwind: mir::UnwindAction,
189189
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
190190

191191
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
@@ -197,7 +197,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
197197
args: &[OpTy<'tcx, Self::Provenance>],
198198
destination: &PlaceTy<'tcx, Self::Provenance>,
199199
target: Option<mir::BasicBlock>,
200-
unwind: StackPopUnwind,
200+
unwind: mir::UnwindAction,
201201
) -> InterpResult<'tcx>;
202202

203203
/// Directly process an intrinsic without pushing a stack frame. It is the hook's
@@ -208,7 +208,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
208208
args: &[OpTy<'tcx, Self::Provenance>],
209209
destination: &PlaceTy<'tcx, Self::Provenance>,
210210
target: Option<mir::BasicBlock>,
211-
unwind: StackPopUnwind,
211+
unwind: mir::UnwindAction,
212212
) -> InterpResult<'tcx>;
213213

214214
/// Called to evaluate `Assert` MIR terminators that trigger a panic.
@@ -487,7 +487,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
487487
_args: &[OpTy<$tcx>],
488488
_destination: &PlaceTy<$tcx, Self::Provenance>,
489489
_target: Option<mir::BasicBlock>,
490-
_unwind: StackPopUnwind,
490+
_unwind: mir::UnwindAction,
491491
) -> InterpResult<$tcx> {
492492
match fn_val {}
493493
}

compiler/rustc_const_eval/src/interpret/mod.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@ mod visitor;
2020

2121
pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in one place: here
2222

23-
pub use self::eval_context::{
24-
Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup, StackPopUnwind,
25-
};
23+
pub use self::eval_context::{Frame, FrameInfo, InterpCx, LocalState, LocalValue, StackPopCleanup};
2624
pub use self::intern::{intern_const_alloc_recursive, InternKind};
2725
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
2826
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};

compiler/rustc_const_eval/src/interpret/terminator.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use rustc_target::spec::abi::Abi;
1313

1414
use super::{
1515
FnVal, ImmTy, Immediate, InterpCx, InterpResult, MPlaceTy, Machine, MemoryKind, OpTy, Operand,
16-
PlaceTy, Scalar, StackPopCleanup, StackPopUnwind,
16+
PlaceTy, Scalar, StackPopCleanup,
1717
};
1818

1919
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
@@ -60,7 +60,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6060
ref args,
6161
destination,
6262
target,
63-
ref unwind,
63+
unwind,
6464
from_hir_call: _,
6565
fn_span: _,
6666
} => {
@@ -106,13 +106,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
106106
with_caller_location,
107107
&destination,
108108
target,
109-
match (unwind, fn_abi.can_unwind) {
110-
(mir::UnwindAction::Cleanup(cleanup), true) => {
111-
StackPopUnwind::Cleanup(*cleanup)
112-
}
113-
(mir::UnwindAction::Continue, true) => StackPopUnwind::Skip,
114-
(_, false) => StackPopUnwind::NotAllowed,
115-
},
109+
if fn_abi.can_unwind { unwind } else { mir::UnwindAction::Unreachable },
116110
)?;
117111
// Sanity-check that `eval_fn_call` either pushed a new frame or
118112
// did a jump to another block.
@@ -353,7 +347,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
353347
with_caller_location: bool,
354348
destination: &PlaceTy<'tcx, M::Provenance>,
355349
target: Option<mir::BasicBlock>,
356-
mut unwind: StackPopUnwind,
350+
mut unwind: mir::UnwindAction,
357351
) -> InterpResult<'tcx> {
358352
trace!("eval_fn_call: {:#?}", fn_val);
359353

@@ -412,9 +406,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
412406
}
413407
}
414408

415-
if !matches!(unwind, StackPopUnwind::NotAllowed) && !callee_fn_abi.can_unwind {
416-
// The callee cannot unwind.
417-
unwind = StackPopUnwind::NotAllowed;
409+
if !callee_fn_abi.can_unwind {
410+
// The callee cannot unwind, so force the `Unreachable` unwind handling.
411+
unwind = mir::UnwindAction::Unreachable;
418412
}
419413

420414
self.push_stack_frame(
@@ -719,10 +713,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
719713
false,
720714
&ret.into(),
721715
Some(target),
722-
match unwind {
723-
mir::UnwindAction::Cleanup(cleanup) => StackPopUnwind::Cleanup(cleanup),
724-
mir::UnwindAction::Continue => StackPopUnwind::Skip,
725-
},
716+
unwind,
726717
)
727718
}
728719
}

compiler/rustc_middle/src/mir/patch.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ pub struct MirPatch<'tcx> {
1212
new_statements: Vec<(Location, StatementKind<'tcx>)>,
1313
new_locals: Vec<LocalDecl<'tcx>>,
1414
resume_block: Option<BasicBlock>,
15+
// Only for unreachable in cleanup path.
16+
unreachable_block: Option<BasicBlock>,
1517
body_span: Span,
1618
next_local: usize,
1719
}
@@ -25,11 +27,12 @@ impl<'tcx> MirPatch<'tcx> {
2527
new_locals: vec![],
2628
next_local: body.local_decls.len(),
2729
resume_block: None,
30+
unreachable_block: None,
2831
body_span: body.span,
2932
};
3033

31-
// Check if we already have a resume block
3234
for (bb, block) in body.basic_blocks.iter_enumerated() {
35+
// Check if we already have a resume block
3336
if let TerminatorKind::Resume = block.terminator().kind && block.statements.is_empty() {
3437
result.resume_block = Some(bb);
3538
break;

0 commit comments

Comments
 (0)