mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-21 22:34:05 +00:00
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:
commit
5526682702
@ -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>;
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
)
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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)*)
|
||||
},
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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 = {
|
||||
|
68
tests/mir-opt/building/custom/unwind_action.rs
Normal file
68
tests/mir-opt/building/custom/unwind_action.rs
Normal 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()
|
||||
}
|
||||
)
|
||||
}
|
34
tests/mir-opt/building/custom/unwind_terminate.rs
Normal file
34
tests/mir-opt/building/custom/unwind_terminate.rs
Normal 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)
|
||||
}
|
||||
)
|
||||
}
|
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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()
|
||||
|
21
tests/ui/mir/validate/noncleanup-cleanup.rs
Normal file
21
tests/ui/mir/validate/noncleanup-cleanup.rs
Normal 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()
|
||||
}
|
||||
)
|
||||
|
||||
}
|
17
tests/ui/mir/validate/noncleanup-resume.rs
Normal file
17
tests/ui/mir/validate/noncleanup-resume.rs
Normal 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()
|
||||
}
|
||||
)
|
||||
}
|
17
tests/ui/mir/validate/noncleanup-terminate.rs
Normal file
17
tests/ui/mir/validate/noncleanup-terminate.rs
Normal 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)
|
||||
}
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user