Auto merge of #117330 - tmiasko:custom-mir-cleanup-blocks, r=cjgillot

Custom MIR: Support cleanup blocks

Cleanup blocks are declared with `bb (cleanup) = { ... }`.

`Call` and `Drop` terminators take an additional argument describing the unwind action, which is one of the following:

* `UnwindContinue()`
* `UnwindUnreachable()`
* `UnwindTerminate(reason)`, where reason is `ReasonAbi` or `ReasonInCleanup`
* `UnwindCleanup(block)`

Also support unwind resume and unwind terminate terminators:

* `UnwindResume()`
* `UnwindTerminate(reason)`
This commit is contained in:
bors 2023-11-14 08:53:25 +00:00
commit 5526682702
38 changed files with 428 additions and 107 deletions

View File

@ -162,6 +162,19 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
expected: expected.to_string(),
}
}
fn stmt_error(&self, stmt: StmtId, expected: &'static str) -> ParseError {
let stmt = &self.thir[stmt];
let span = match stmt.kind {
StmtKind::Expr { expr, .. } => self.thir[expr].span,
StmtKind::Let { span, .. } => span,
};
ParseError {
span,
item_description: format!("{:?}", stmt.kind),
expected: expected.to_string(),
}
}
}
type PResult<T> = Result<T, ParseError>;

View File

@ -27,10 +27,13 @@ macro_rules! parse_by_kind {
$expr_name:pat,
$expected:literal,
$(
@call($name:literal, $args:ident) => $call_expr:expr,
@call($name:ident, $args:ident) => $call_expr:expr,
)*
$(
$pat:pat => $expr:expr,
@variant($adt:ident, $variant:ident) => $variant_expr:expr,
)*
$(
$pat:pat $(if $guard:expr)? => $expr:expr,
)*
) => {{
let expr_id = $self.preparse($expr_id);
@ -42,14 +45,20 @@ macro_rules! parse_by_kind {
ExprKind::Call { ty, fun: _, args: $args, .. } if {
match ty.kind() {
ty::FnDef(did, _) => {
$self.tcx.is_diagnostic_item(rustc_span::Symbol::intern($name), *did)
$self.tcx.is_diagnostic_item(rustc_span::sym::$name, *did)
}
_ => false,
}
} => $call_expr,
)*
$(
$pat => $expr,
ExprKind::Adt(box AdtExpr { adt_def, variant_index, .. }) if {
$self.tcx.is_diagnostic_item(rustc_span::sym::$adt, adt_def.did()) &&
adt_def.variants()[*variant_index].name == rustc_span::sym::$variant
} => $variant_expr,
)*
$(
$pat $(if $guard)? => $expr,
)*
#[allow(unreachable_patterns)]
_ => return Err($self.expr_error(expr_id, $expected))
@ -172,7 +181,8 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
ExprKind::Block { block } => &self.thir[*block].stmts,
);
for (i, block_def) in block_defs.iter().enumerate() {
let block = self.parse_block_def(self.statement_as_expr(*block_def)?)?;
let is_cleanup = self.body.basic_blocks_mut()[BasicBlock::from_usize(i)].is_cleanup;
let block = self.parse_block_def(self.statement_as_expr(*block_def)?, is_cleanup)?;
self.body.basic_blocks_mut()[BasicBlock::from_usize(i)] = block;
}
@ -181,15 +191,28 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
fn parse_block_decls(&mut self, stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
for stmt in stmts {
let (var, _, _) = self.parse_let_statement(stmt)?;
let data = BasicBlockData::new(None);
let block = self.body.basic_blocks_mut().push(data);
self.block_map.insert(var, block);
self.parse_basic_block_decl(stmt)?;
}
Ok(())
}
fn parse_basic_block_decl(&mut self, stmt: StmtId) -> PResult<()> {
match &self.thir[stmt].kind {
StmtKind::Let { pattern, initializer: Some(initializer), .. } => {
let (var, ..) = self.parse_var(pattern)?;
let mut data = BasicBlockData::new(None);
data.is_cleanup = parse_by_kind!(self, *initializer, _, "basic block declaration",
@variant(mir_basic_block, Normal) => false,
@variant(mir_basic_block, Cleanup) => true,
);
let block = self.body.basic_blocks_mut().push(data);
self.block_map.insert(var, block);
Ok(())
}
_ => Err(self.stmt_error(stmt, "let statement with an initializer")),
}
}
fn parse_local_decls(&mut self, mut stmts: impl Iterator<Item = StmtId>) -> PResult<()> {
let (ret_var, ..) = self.parse_let_statement(stmts.next().unwrap())?;
self.local_map.insert(ret_var, Local::from_u32(0));
@ -219,7 +242,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
};
let span = self.thir[expr].span;
let (name, operand) = parse_by_kind!(self, expr, _, "debuginfo",
@call("mir_debuginfo", args) => {
@call(mir_debuginfo, args) => {
(args[0], args[1])
},
);
@ -281,12 +304,13 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
}
}
fn parse_block_def(&self, expr_id: ExprId) -> PResult<BasicBlockData<'tcx>> {
fn parse_block_def(&self, expr_id: ExprId, is_cleanup: bool) -> PResult<BasicBlockData<'tcx>> {
let block = parse_by_kind!(self, expr_id, _, "basic block",
ExprKind::Block { block } => &self.thir[*block],
);
let mut data = BasicBlockData::new(None);
data.is_cleanup = is_cleanup;
for stmt_id in &*block.stmts {
let stmt = self.statement_as_expr(*stmt_id)?;
let span = self.thir[stmt].span;

View File

@ -13,19 +13,19 @@ use super::{parse_by_kind, PResult, ParseCtxt};
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
pub fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
parse_by_kind!(self, expr_id, _, "statement",
@call("mir_storage_live", args) => {
@call(mir_storage_live, args) => {
Ok(StatementKind::StorageLive(self.parse_local(args[0])?))
},
@call("mir_storage_dead", args) => {
@call(mir_storage_dead, args) => {
Ok(StatementKind::StorageDead(self.parse_local(args[0])?))
},
@call("mir_deinit", args) => {
@call(mir_deinit, args) => {
Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?)))
},
@call("mir_retag", args) => {
@call(mir_retag, args) => {
Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?)))
},
@call("mir_set_discriminant", args) => {
@call(mir_set_discriminant, args) => {
let place = self.parse_place(args[0])?;
let var = self.parse_integer_literal(args[1])? as u32;
Ok(StatementKind::SetDiscriminant {
@ -43,24 +43,30 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
pub fn parse_terminator(&self, expr_id: ExprId) -> PResult<TerminatorKind<'tcx>> {
parse_by_kind!(self, expr_id, expr, "terminator",
@call("mir_return", _args) => {
@call(mir_return, _args) => {
Ok(TerminatorKind::Return)
},
@call("mir_goto", args) => {
@call(mir_goto, args) => {
Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
},
@call("mir_unreachable", _args) => {
@call(mir_unreachable, _args) => {
Ok(TerminatorKind::Unreachable)
},
@call("mir_drop", args) => {
@call(mir_unwind_resume, _args) => {
Ok(TerminatorKind::UnwindResume)
},
@call(mir_unwind_terminate, args) => {
Ok(TerminatorKind::UnwindTerminate(self.parse_unwind_terminate_reason(args[0])?))
},
@call(mir_drop, args) => {
Ok(TerminatorKind::Drop {
place: self.parse_place(args[0])?,
target: self.parse_block(args[1])?,
unwind: UnwindAction::Continue,
unwind: self.parse_unwind_action(args[2])?,
replace: false,
})
},
@call("mir_call", args) => {
@call(mir_call, args) => {
self.parse_call(args)
},
ExprKind::Match { scrutinee, arms, .. } => {
@ -70,6 +76,34 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
)
}
fn parse_unwind_terminate_reason(&self, expr_id: ExprId) -> PResult<UnwindTerminateReason> {
parse_by_kind!(self, expr_id, _, "unwind terminate reason",
@variant(mir_unwind_terminate_reason, Abi) => {
Ok(UnwindTerminateReason::Abi)
},
@variant(mir_unwind_terminate_reason, InCleanup) => {
Ok(UnwindTerminateReason::InCleanup)
},
)
}
fn parse_unwind_action(&self, expr_id: ExprId) -> PResult<UnwindAction> {
parse_by_kind!(self, expr_id, _, "unwind action",
@call(mir_unwind_continue, _args) => {
Ok(UnwindAction::Continue)
},
@call(mir_unwind_unreachable, _args) => {
Ok(UnwindAction::Unreachable)
},
@call(mir_unwind_terminate, args) => {
Ok(UnwindAction::Terminate(self.parse_unwind_terminate_reason(args[0])?))
},
@call(mir_unwind_cleanup, args) => {
Ok(UnwindAction::Cleanup(self.parse_block(args[0])?))
},
)
}
fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult<SwitchTargets> {
let Some((otherwise, rest)) = arms.split_last() else {
return Err(ParseError {
@ -113,6 +147,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
);
let destination = self.parse_place(destination)?;
let target = self.parse_block(args[1])?;
let unwind = self.parse_unwind_action(args[2])?;
parse_by_kind!(self, call, _, "function call",
ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => {
@ -126,7 +161,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
args,
destination,
target: Some(target),
unwind: UnwindAction::Continue,
unwind,
call_source: if *from_hir_call { CallSource::Normal } else {
CallSource::OverloadedOperator
},
@ -138,25 +173,25 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
parse_by_kind!(self, expr_id, expr, "rvalue",
@call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
@call("mir_cast_transmute", args) => {
@call(mir_discriminant, args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
@call(mir_cast_transmute, args) => {
let source = self.parse_operand(args[0])?;
Ok(Rvalue::Cast(CastKind::Transmute, source, expr.ty))
},
@call("mir_checked", args) => {
@call(mir_checked, args) => {
parse_by_kind!(self, args[0], _, "binary op",
ExprKind::Binary { op, lhs, rhs } => Ok(Rvalue::CheckedBinaryOp(
*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))
)),
)
},
@call("mir_offset", args) => {
@call(mir_offset, args) => {
let ptr = self.parse_operand(args[0])?;
let offset = self.parse_operand(args[1])?;
Ok(Rvalue::BinaryOp(BinOp::Offset, Box::new((ptr, offset))))
},
@call("mir_len", args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
@call("mir_copy_for_deref", args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)),
@call(mir_len, args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
@call(mir_copy_for_deref, args) => Ok(Rvalue::CopyForDeref(self.parse_place(args[0])?)),
ExprKind::Borrow { borrow_kind, arg } => Ok(
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
),
@ -206,9 +241,9 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
pub fn parse_operand(&self, expr_id: ExprId) -> PResult<Operand<'tcx>> {
parse_by_kind!(self, expr_id, expr, "operand",
@call("mir_move", args) => self.parse_place(args[0]).map(Operand::Move),
@call("mir_static", args) => self.parse_static(args[0]),
@call("mir_static_mut", args) => self.parse_static(args[0]),
@call(mir_move, args) => self.parse_place(args[0]).map(Operand::Move),
@call(mir_static, args) => self.parse_static(args[0]),
@call(mir_static_mut, args) => self.parse_static(args[0]),
ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
@ -229,7 +264,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
fn parse_place_inner(&self, expr_id: ExprId) -> PResult<(Place<'tcx>, PlaceTy<'tcx>)> {
let (parent, proj) = parse_by_kind!(self, expr_id, expr, "place",
@call("mir_field", args) => {
@call(mir_field, args) => {
let (parent, ty) = self.parse_place_inner(args[0])?;
let field = FieldIdx::from_u32(self.parse_integer_literal(args[1])? as u32);
let field_ty = ty.field_ty(self.tcx, field);
@ -237,7 +272,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
let place = parent.project_deeper(&[proj], self.tcx);
return Ok((place, PlaceTy::from_ty(field_ty)));
},
@call("mir_variant", args) => {
@call(mir_variant, args) => {
(args[0], PlaceElem::Downcast(
None,
VariantIdx::from_u32(self.parse_integer_literal(args[1])? as u32)
@ -245,7 +280,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
},
ExprKind::Deref { arg } => {
parse_by_kind!(self, *arg, _, "does not matter",
@call("mir_make_place", args) => return self.parse_place_inner(args[0]),
@call(mir_make_place, args) => return self.parse_place_inner(args[0]),
_ => (*arg, PlaceElem::Deref),
)
},

View File

@ -124,6 +124,7 @@ symbols! {
// There is currently no checking that all symbols are used; that would be
// nice to have.
Symbols {
Abi,
AcqRel,
Acquire,
AddToDiagnostic,
@ -166,6 +167,7 @@ symbols! {
CString,
Capture,
Center,
Cleanup,
Clone,
Command,
ConstParamTy,
@ -215,6 +217,7 @@ symbols! {
HashSet,
Hasher,
Implied,
InCleanup,
IndexOutput,
Input,
Instant,
@ -258,6 +261,7 @@ symbols! {
NonZeroU8,
NonZeroUsize,
None,
Normal,
Ok,
Option,
Ord,
@ -1023,6 +1027,36 @@ symbols! {
minnumf32,
minnumf64,
mips_target_feature,
mir_basic_block,
mir_call,
mir_cast_transmute,
mir_checked,
mir_copy_for_deref,
mir_debuginfo,
mir_deinit,
mir_discriminant,
mir_drop,
mir_field,
mir_goto,
mir_len,
mir_make_place,
mir_move,
mir_offset,
mir_retag,
mir_return,
mir_set_discriminant,
mir_static,
mir_static_mut,
mir_storage_dead,
mir_storage_live,
mir_unreachable,
mir_unwind_cleanup,
mir_unwind_continue,
mir_unwind_resume,
mir_unwind_terminate,
mir_unwind_terminate_reason,
mir_unwind_unreachable,
mir_variant,
miri,
misc,
mmx_reg,

View File

@ -110,15 +110,15 @@
//! let popped;
//!
//! {
//! Call(_unused = Vec::push(v, value), pop)
//! Call(_unused = Vec::push(v, value), pop, UnwindContinue())
//! }
//!
//! pop = {
//! Call(popped = Vec::pop(v), drop)
//! Call(popped = Vec::pop(v), drop, UnwindContinue())
//! }
//!
//! drop = {
//! Drop(popped, ret)
//! Drop(popped, ret, UnwindContinue())
//! }
//!
//! ret = {
@ -238,10 +238,6 @@
//!
//! #### Terminators
//!
//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
//! are no resume and abort terminators, and terminators that might unwind do not have any way to
//! indicate the unwind block.
//!
//! - [`Goto`], [`Return`], [`Unreachable`] and [`Drop`](Drop()) have associated functions.
//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
@ -260,7 +256,26 @@
/// Type representing basic blocks.
///
/// All terminators will have this type as a return type. It helps achieve some type safety.
pub struct BasicBlock;
#[rustc_diagnostic_item = "mir_basic_block"]
pub enum BasicBlock {
/// A non-cleanup basic block.
Normal,
/// A basic block that lies on an unwind path.
Cleanup,
}
/// The reason we are terminating the process during unwinding.
#[rustc_diagnostic_item = "mir_unwind_terminate_reason"]
pub enum UnwindTerminateReason {
/// Unwinding is just not possible given the ABI of this function.
Abi,
/// We were already cleaning up for an ongoing unwind, and a *second*, *nested* unwind was
/// triggered by the drop glue.
InCleanup,
}
pub use UnwindTerminateReason::Abi as ReasonAbi;
pub use UnwindTerminateReason::InCleanup as ReasonInCleanup;
macro_rules! define {
($name:literal, $( #[ $meta:meta ] )* fn $($sig:tt)*) => {
@ -271,11 +286,41 @@ macro_rules! define {
}
}
// Unwind actions
define!(
"mir_unwind_continue",
/// An unwind action that continues unwinding.
fn UnwindContinue()
);
define!(
"mir_unwind_unreachable",
/// An unwind action that triggers undefined behaviour.
fn UnwindUnreachable() -> BasicBlock
);
define!(
"mir_unwind_terminate",
/// An unwind action that terminates the execution.
///
/// `UnwindTerminate` can also be used as a terminator.
fn UnwindTerminate(reason: UnwindTerminateReason)
);
define!(
"mir_unwind_cleanup",
/// An unwind action that continues execution in a given basic blok.
fn UnwindCleanup(goto: BasicBlock)
);
// Terminators
define!("mir_return", fn Return() -> BasicBlock);
define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
define!("mir_unreachable", fn Unreachable() -> BasicBlock);
define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
define!("mir_call", fn Call(call: (), goto: BasicBlock));
define!("mir_drop", fn Drop<T, U>(place: T, goto: BasicBlock, unwind_action: U));
define!("mir_call", fn Call<U>(call: (), goto: BasicBlock, unwind_action: U));
define!("mir_unwind_resume",
/// A terminator that resumes the unwinding.
fn UnwindResume()
);
define!("mir_storage_live", fn StorageLive<T>(local: T));
define!("mir_storage_dead", fn StorageDead<T>(local: T));
define!("mir_deinit", fn Deinit<T>(place: T));
@ -382,16 +427,15 @@ pub macro mir {
}
$(
$block_name:ident = {
$block_name:ident $(($block_cleanup:ident))? = {
$($block:tt)*
}
)*
) => {{
// First, we declare all basic blocks.
$(
let $block_name: ::core::intrinsics::mir::BasicBlock;
)*
__internal_declare_basic_blocks!($(
$block_name $(($block_cleanup))?
)*);
{
// Now all locals
#[allow(non_snake_case)]
@ -585,3 +629,17 @@ pub macro __internal_remove_let {
}
},
}
/// Helper macro that declares the basic blocks.
#[doc(hidden)]
pub macro __internal_declare_basic_blocks {
() => {},
($name:ident (cleanup) $($rest:tt)*) => {
let $name = ::core::intrinsics::mir::BasicBlock::Cleanup;
__internal_declare_basic_blocks!($($rest)*)
},
($name:ident $($rest:tt)*) => {
let $name = ::core::intrinsics::mir::BasicBlock::Normal;
__internal_declare_basic_blocks!($($rest)*)
},
}

View File

@ -14,7 +14,7 @@ fn main() {
let ptr = std::ptr::addr_of_mut!(non_copy);
// Inside `callee`, the first argument and `*ptr` are basically
// aliasing places!
Call(_unit = callee(Move(*ptr), ptr), after_call)
Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue())
}
after_call = {
Return()

View File

@ -27,8 +27,8 @@ LL | unsafe { ptr.write(S(0)) };
note: inside `main`
--> $DIR/arg_inplace_mutate.rs:LL:CC
|
LL | Call(_unit = callee(Move(*ptr), ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -35,8 +35,8 @@ LL | unsafe { ptr.write(S(0)) };
note: inside `main`
--> $DIR/arg_inplace_mutate.rs:LL:CC
|
LL | Call(_unit = callee(Move(*ptr), ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_unit = callee(Move(*ptr), ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -11,7 +11,7 @@ fn main() {
{
let non_copy = S(42);
// This could change `non_copy` in-place
Call(_unit = change_arg(Move(non_copy)), after_call)
Call(_unit = change_arg(Move(non_copy)), after_call, UnwindContinue())
}
after_call = {
// So now we must not be allowed to observe non-copy again.

View File

@ -11,8 +11,8 @@ LL | unsafe { ptr.read() };
note: inside `main`
--> $DIR/arg_inplace_observe_during.rs:LL:CC
|
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -14,7 +14,7 @@ fn main() {
let non_copy = S(42);
let ptr = std::ptr::addr_of_mut!(non_copy);
// This could change `non_copy` in-place
Call(_unit = change_arg(Move(*ptr), ptr), after_call)
Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue())
}
after_call = {
Return()

View File

@ -27,8 +27,8 @@ LL | x.0 = 0;
note: inside `main`
--> $DIR/arg_inplace_observe_during.rs:LL:CC
|
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -35,8 +35,8 @@ LL | x.0 = 0;
note: inside `main`
--> $DIR/arg_inplace_observe_during.rs:LL:CC
|
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_unit = change_arg(Move(*ptr), ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -11,8 +11,8 @@ LL | unsafe { ptr.read() };
note: inside `main`
--> $DIR/return_pointer_aliasing.rs:LL:CC
|
LL | Call(*ptr = myfun(ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -15,7 +15,7 @@ pub fn main() {
let ptr = &raw mut x;
// We arrange for `myfun` to have a pointer that aliases
// its return place. Even just reading from that pointer is UB.
Call(*ptr = myfun(ptr), after_call)
Call(*ptr = myfun(ptr), after_call, UnwindContinue())
}
after_call = {

View File

@ -27,8 +27,8 @@ LL | unsafe { ptr.read() };
note: inside `main`
--> $DIR/return_pointer_aliasing.rs:LL:CC
|
LL | Call(*ptr = myfun(ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -35,8 +35,8 @@ LL | unsafe { ptr.read() };
note: inside `main`
--> $DIR/return_pointer_aliasing.rs:LL:CC
|
LL | Call(*ptr = myfun(ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(*ptr = myfun(ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -15,7 +15,7 @@ pub fn main() {
let ptr = &raw mut _x;
// We arrange for `myfun` to have a pointer that aliases
// its return place. Even just reading from that pointer is UB.
Call(_x = myfun(ptr), after_call)
Call(_x = myfun(ptr), after_call, UnwindContinue())
}
after_call = {

View File

@ -30,8 +30,8 @@ LL | unsafe { ptr.write(0) };
note: inside `main`
--> $DIR/return_pointer_aliasing2.rs:LL:CC
|
LL | Call(_x = myfun(ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_x = myfun(ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -35,8 +35,8 @@ LL | unsafe { ptr.write(0) };
note: inside `main`
--> $DIR/return_pointer_aliasing2.rs:LL:CC
|
LL | Call(_x = myfun(ptr), after_call)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | Call(_x = myfun(ptr), after_call, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -14,7 +14,7 @@ struct S(i32, [u8; 128]);
fn docall(out: &mut S) {
mir! {
{
Call(*out = callee(), after_call)
Call(*out = callee(), after_call, UnwindContinue())
}
after_call = {
@ -37,7 +37,7 @@ fn callee() -> S {
// become visible to the outside. In codegen we can see them
// but Miri should detect this as UB!
RET.0 = 42;
Call(_unit = startpanic(), after_call)
Call(_unit = startpanic(), after_call, UnwindContinue())
}
after_call = {

View File

@ -20,7 +20,7 @@ fn call(f: fn(NonZeroU32)) {
let tmp = ptr::addr_of!(c);
let ptr = tmp as *const NonZeroU32;
// The call site now is a NonZeroU32-to-u32 transmute.
Call(_res = f(*ptr), retblock) //~ERROR: expected something greater or equal to 1
Call(_res = f(*ptr), retblock, UnwindContinue()) //~ERROR: expected something greater or equal to 1
}
retblock = {
Return()

View File

@ -1,8 +1,8 @@
error: Undefined Behavior: constructing invalid value: encountered 0, but expected something greater or equal to 1
--> $DIR/cast_fn_ptr_invalid_caller_arg.rs:LL:CC
|
LL | Call(_res = f(*ptr), retblock)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
LL | Call(_res = f(*ptr), retblock, UnwindContinue())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0, but expected something greater or equal to 1
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

View File

@ -11,7 +11,7 @@ pub fn main() {
{
let x = 0;
let ptr = &raw mut x;
Call(*ptr = myfun(), after_call)
Call(*ptr = myfun(), after_call, UnwindContinue())
}
after_call = {

View File

@ -13,7 +13,7 @@ fn ident<T>(t: T) -> T {
fn direct_call(x: i32) -> i32 {
mir!(
{
Call(RET = ident(x), retblock)
Call(RET = ident(x), retblock, UnwindContinue())
}
retblock = {
@ -27,7 +27,7 @@ fn direct_call(x: i32) -> i32 {
fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 {
mir!(
{
Call(RET = f(x), retblock)
Call(RET = f(x), retblock, UnwindContinue())
}
retblock = {
@ -49,7 +49,7 @@ impl<'a> Drop for WriteOnDrop<'a> {
fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
mir!(
{
Drop(a, retblock)
Drop(a, retblock, UnwindContinue())
}
retblock = {
@ -64,7 +64,7 @@ fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
mir!(
{
Drop(b, retblock)
Drop(b, retblock, UnwindContinue())
}
retblock = {

View File

@ -0,0 +1,68 @@
// compile-flags: --crate-type=lib
// edition:2021
// needs-unwind
#![feature(custom_mir, core_intrinsics)]
use core::intrinsics::mir::*;
// CHECK-LABEL: fn a()
// CHECK: bb0: {
// CHECK-NEXT: a() -> [return: bb1, unwind unreachable];
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn a() {
mir!(
{
Call(RET = a(), bb1, UnwindUnreachable())
}
bb1 = {
Return()
}
)
}
// CHECK-LABEL: fn b()
// CHECK: bb0: {
// CHECK-NEXT: b() -> [return: bb1, unwind continue];
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn b() {
mir!(
{
Call(RET = b(), bb1, UnwindContinue())
}
bb1 = {
Return()
}
)
}
// CHECK-LABEL: fn c()
// CHECK: bb0: {
// CHECK-NEXT: c() -> [return: bb1, unwind terminate(abi)];
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn c() {
mir!(
{
Call(RET = c(), bb1, UnwindTerminate(ReasonAbi))
}
bb1 = {
Return()
}
)
}
// CHECK-LABEL: fn d()
// CHECK: bb0: {
// CHECK-NEXT: d() -> [return: bb1, unwind: bb2];
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn d() {
mir!(
{
Call(RET = d(), bb1, UnwindCleanup(bb2))
}
bb1 = {
Return()
}
bb2 (cleanup) = {
UnwindResume()
}
)
}

View File

@ -0,0 +1,34 @@
// compile-flags: --crate-type=lib
// edition:2021
#![feature(custom_mir, core_intrinsics)]
use core::intrinsics::mir::*;
// CHECK-LABEL: fn f()
// CHECK: bb1 (cleanup): {
// CHECK-NEXT: terminate(abi);
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn f() {
mir!(
{
Return()
}
bb1(cleanup) = {
UnwindTerminate(ReasonAbi)
}
)
}
// CHECK-LABEL: fn g()
// CHECK: bb1 (cleanup): {
// CHECK-NEXT: terminate(cleanup);
#[custom_mir(dialect = "runtime", phase = "optimized")]
pub fn g() {
mir!(
{
Return()
}
bb1(cleanup) = {
UnwindTerminate(ReasonInCleanup)
}
)
}

View File

@ -22,11 +22,11 @@ fn f() -> bool {
let b = a;
// We cannot propagate the place `a`.
let r2 = &b;
Call(RET = cmp_ref(r1, r2), next)
Call(RET = cmp_ref(r1, r2), next, UnwindContinue())
}
next = {
// But we can propagate the value `a`.
Call(RET = opaque(b), ret)
Call(RET = opaque(b), ret, UnwindContinue())
}
ret = {
Return()

View File

@ -26,7 +26,7 @@ fn multiple_edges(t: bool) -> u8 {
match t { true => bbt, _ => ret }
}
bbt = {
Call(x = dummy(13), ret)
Call(x = dummy(13), ret, UnwindContinue())
}
ret = {
// `x` is not assigned on the `bb0 -> ret` edge,

View File

@ -14,11 +14,11 @@ struct NotCopy(bool);
fn f(_1: NotCopy) {
mir!({
let _2 = _1;
Call(RET = opaque(Move(_1)), bb1)
Call(RET = opaque(Move(_1)), bb1, UnwindContinue())
}
bb1 = {
let _3 = Move(_2);
Call(RET = opaque(_3), bb2)
Call(RET = opaque(_3), bb2, UnwindContinue())
}
bb2 = {
Return()

View File

@ -18,10 +18,10 @@ fn f(a: Foo) -> bool {
let b = a;
// This is a move out of a copy, so must become a copy of `a.0`.
let c = Move(b.0);
Call(RET = opaque(Move(a)), bb1)
Call(RET = opaque(Move(a)), bb1, UnwindContinue())
}
bb1 = {
Call(RET = opaque(Move(c)), ret)
Call(RET = opaque(Move(c)), ret, UnwindContinue())
}
ret = {
Return()

View File

@ -28,7 +28,7 @@ struct Packed {
fn move_packed(packed: Packed) {
mir!(
{
Call(RET = use_both(0, packed.y), ret)
Call(RET = use_both(0, packed.y), ret, UnwindContinue())
}
ret = {
Return()

View File

@ -20,7 +20,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) {
mir!(
let condition: bool;
{
Call(condition = cond(), bb1)
Call(condition = cond(), bb1, UnwindContinue())
}
bb1 = {
match condition { true => bb2, _ => ret }
@ -30,7 +30,7 @@ fn cycle(mut x: i32, mut y: i32, mut z: i32) {
z = y;
y = x;
x = temp;
Call(condition = cond(), bb1)
Call(condition = cond(), bb1, UnwindContinue())
}
ret = {
Return()

View File

@ -529,31 +529,31 @@ fn duplicate_slice() -> (bool, bool) {
// CHECK: [[a:_.*]] = (const "a",);
// CHECK: [[au:_.*]] = ([[a]].0: &str) as u128 (Transmute);
let a = ("a",);
Call(au = transmute::<_, u128>(a.0), bb1)
Call(au = transmute::<_, u128>(a.0), bb1, UnwindContinue())
}
bb1 = {
// CHECK: [[c:_.*]] = identity::<&str>(([[a]].0: &str))
Call(c = identity(a.0), bb2)
Call(c = identity(a.0), bb2, UnwindContinue())
}
bb2 = {
// CHECK: [[cu:_.*]] = [[c]] as u128 (Transmute);
Call(cu = transmute::<_, u128>(c), bb3)
Call(cu = transmute::<_, u128>(c), bb3, UnwindContinue())
}
bb3 = {
// This slice is different from `a.0`. Hence `bu` is not `au`.
// CHECK: [[b:_.*]] = const "a";
// CHECK: [[bu:_.*]] = [[b]] as u128 (Transmute);
let b = "a";
Call(bu = transmute::<_, u128>(b), bb4)
Call(bu = transmute::<_, u128>(b), bb4, UnwindContinue())
}
bb4 = {
// This returns a copy of `b`, which is not `a`.
// CHECK: [[d:_.*]] = identity::<&str>([[b]])
Call(d = identity(b), bb5)
Call(d = identity(b), bb5, UnwindContinue())
}
bb5 = {
// CHECK: [[du:_.*]] = [[d]] as u128 (Transmute);
Call(du = transmute::<_, u128>(d), bb6)
Call(du = transmute::<_, u128>(d), bb6, UnwindContinue())
}
bb6 = {
// `direct` must not fold to `true`, as `indirect` will not.

View File

@ -695,7 +695,7 @@ fn multiple_storage() {
// As there are multiple `StorageLive` statements for `x`, we cannot know if this `z`'s
// pointer address is the address of `x`, so do nothing.
let y = *z;
Call(RET = opaque(y), retblock)
Call(RET = opaque(y), retblock, UnwindContinue())
}
retblock = {
@ -723,7 +723,7 @@ fn dominate_storage() {
}
bb1 = {
let c = *r;
Call(RET = opaque(c), bb2)
Call(RET = opaque(c), bb2, UnwindContinue())
}
bb2 = {
StorageDead(x);
@ -759,18 +759,18 @@ fn maybe_dead(m: bool) {
bb1 = {
StorageDead(x);
StorageDead(y);
Call(RET = opaque(u), bb2)
Call(RET = opaque(u), bb2, UnwindContinue())
}
bb2 = {
// As `x` may be `StorageDead`, `a` may be dangling, so we do nothing.
let z = *a;
Call(RET = opaque(z), bb3)
Call(RET = opaque(z), bb3, UnwindContinue())
}
bb3 = {
// As `y` may be `StorageDead`, `b` may be dangling, so we do nothing.
// This implies that we also do not substitute `b` in `bb0`.
let t = *b;
Call(RET = opaque(t), retblock)
Call(RET = opaque(t), retblock, UnwindContinue())
}
retblock = {
Return()

View File

@ -0,0 +1,21 @@
// Check that validation rejects cleanup edge to a non-cleanup block.
//
// failure-status: 101
// dont-check-compiler-stderr
// error-pattern: cleanuppad mismatch
#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
pub fn main() {
mir!(
{
Call(RET = main(), block, UnwindCleanup(block))
}
block = {
Return()
}
)
}

View File

@ -0,0 +1,17 @@
// Check that validation rejects resume terminator in a non-cleanup block.
//
// failure-status: 101
// dont-check-compiler-stderr
// error-pattern: resume on non-cleanup block
#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
pub fn main() {
mir!(
{
UnwindResume()
}
)
}

View File

@ -0,0 +1,17 @@
// Check that validation rejects terminate terminator in a non-cleanup block.
//
// failure-status: 101
// dont-check-compiler-stderr
// error-pattern: terminate on non-cleanup block
#![feature(custom_mir, core_intrinsics)]
extern crate core;
use core::intrinsics::mir::*;
#[custom_mir(dialect = "built")]
pub fn main() {
mir!(
{
UnwindTerminate(ReasonAbi)
}
)
}