mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-01 04:27:38 +00:00
Auto merge of #86155 - alexcrichton:abort-on-unwind, r=nikomatsakis
rustc: Fill out remaining parts of C-unwind ABI This commit intends to fill out some of the remaining pieces of the C-unwind ABI. This has a number of other changes with it though to move this design space forward a bit. Notably contained within here is: * On `panic=unwind`, the `extern "C"` ABI is now considered as "may unwind". This fixes a longstanding soundness issue where if you `panic!()` in an `extern "C"` function defined in Rust that's actually UB because the LLVM representation for the function has the `nounwind` attribute, but then you unwind. * Whether or not a function unwinds now mainly considers the ABI of the function instead of first checking the panic strategy. This fixes a miscompile of `extern "C-unwind"` with `panic=abort` because that ABI can still unwind. * The aborting stub for non-unwinding ABIs with `panic=unwind` has been reimplemented. Previously this was done as a small tweak during MIR generation, but this has been moved to a separate and dedicated MIR pass. This new pass will, for appropriate functions and function calls, insert a `cleanup` landing pad for any function call that may unwind within a function that is itself not allowed to unwind. Note that this subtly changes some behavior from before where previously on an unwind which was caught-to-abort it would run active destructors in the function, and now it simply immediately aborts the process. * The `#[unwind]` attribute has been removed and all users in tests and such are now using `C-unwind` and `#![feature(c_unwind)]`. I think this is largely the last piece of the RFC to implement. Unfortunately I believe this is still not stabilizable as-is because activating the feature gate changes the behavior of the existing `extern "C"` ABI in a way that has no replacement. My thinking for how to enable this is that we add support for the `C-unwind` ABI on stable Rust first, and then after it hits stable we change the behavior of the `C` ABI. That way anyone straddling stable/beta/nightly can switch to `C-unwind` safely.
This commit is contained in:
commit
25b7648496
@ -87,50 +87,6 @@ pub enum OptimizeAttr {
|
||||
Size,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum UnwindAttr {
|
||||
Allowed,
|
||||
Aborts,
|
||||
}
|
||||
|
||||
/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
|
||||
pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
|
||||
attrs.iter().fold(None, |ia, attr| {
|
||||
if sess.check_name(attr, sym::unwind) {
|
||||
if let Some(meta) = attr.meta() {
|
||||
if let MetaItemKind::List(items) = meta.kind {
|
||||
if items.len() == 1 {
|
||||
if items[0].has_name(sym::allowed) {
|
||||
return Some(UnwindAttr::Allowed);
|
||||
} else if items[0].has_name(sym::aborts) {
|
||||
return Some(UnwindAttr::Aborts);
|
||||
}
|
||||
}
|
||||
|
||||
struct_span_err!(
|
||||
sess.diagnostic(),
|
||||
attr.span,
|
||||
E0633,
|
||||
"malformed `unwind` attribute input"
|
||||
)
|
||||
.span_label(attr.span, "invalid argument")
|
||||
.span_suggestions(
|
||||
attr.span,
|
||||
"the allowed arguments are `allowed` and `aborts`",
|
||||
(vec!["allowed", "aborts"])
|
||||
.into_iter()
|
||||
.map(|s| format!("#[unwind({})]", s)),
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ia
|
||||
})
|
||||
}
|
||||
|
||||
/// Represents the following attributes:
|
||||
///
|
||||
/// - `#[stable]`
|
||||
|
@ -1,8 +1,10 @@
|
||||
#### Note: this error code is no longer emitted by the compiler.
|
||||
|
||||
The `unwind` attribute was malformed.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0633
|
||||
```compile_fail
|
||||
#![feature(unwind_attributes)]
|
||||
|
||||
#[unwind()] // error: expected one argument
|
||||
|
@ -311,11 +311,6 @@ declare_features! (
|
||||
/// Allows `extern "platform-intrinsic" { ... }`.
|
||||
(active, platform_intrinsics, "1.4.0", Some(27731), None),
|
||||
|
||||
/// Allows `#[unwind(..)]`.
|
||||
///
|
||||
/// Permits specifying whether a function should permit unwinding or abort on unwind.
|
||||
(active, unwind_attributes, "1.4.0", Some(58760), None),
|
||||
|
||||
/// Allows attributes on expressions and non-item statements.
|
||||
(active, stmt_expr_attributes, "1.6.0", Some(15701), None),
|
||||
|
||||
|
@ -419,10 +419,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
||||
),
|
||||
gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)),
|
||||
gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)),
|
||||
gated!(
|
||||
unwind, AssumedUsed, template!(List: "allowed|aborts"), unwind_attributes,
|
||||
experimental!(unwind),
|
||||
),
|
||||
gated!(
|
||||
compiler_builtins, AssumedUsed, template!(Word),
|
||||
"the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
|
||||
|
@ -156,6 +156,11 @@ declare_features! (
|
||||
(removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None,
|
||||
Some("removed in favor of full type_alias_impl_trait")),
|
||||
|
||||
/// Allows `#[unwind(..)]`.
|
||||
///
|
||||
/// Permits specifying whether a function should permit unwinding or abort on unwind.
|
||||
(removed, unwind_attributes, "1.56.0", Some(58760), None, Some("use the C-unwind ABI instead")),
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: removed features
|
||||
// -------------------------------------------------------------------------
|
||||
|
@ -52,13 +52,9 @@ bitflags! {
|
||||
/// `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this
|
||||
/// function is never null.
|
||||
const ALLOCATOR = 1 << 1;
|
||||
/// `#[unwind]`: an indicator that this function may unwind despite what
|
||||
/// its ABI signature may otherwise imply.
|
||||
const UNWIND = 1 << 2;
|
||||
/// `#[rust_allocator_nounwind]`, an indicator that an imported FFI
|
||||
/// function will never unwind. Probably obsolete by recent changes with
|
||||
/// #[unwind], but hasn't been removed/migrated yet
|
||||
const RUSTC_ALLOCATOR_NOUNWIND = 1 << 3;
|
||||
/// An indicator that function will never unwind. Will become obsolete
|
||||
/// once C-unwind is fully stabilized.
|
||||
const NEVER_UNWIND = 1 << 3;
|
||||
/// `#[naked]`: an indicator to LLVM that no function prologue/epilogue
|
||||
/// should be generated.
|
||||
const NAKED = 1 << 4;
|
||||
|
@ -237,7 +237,7 @@ pub enum TerminatorKind<'tcx> {
|
||||
/// consider it in borrowck. We don't want to accept programs which
|
||||
/// pass borrowck only when `panic=abort` or some assertions are disabled
|
||||
/// due to release vs. debug mode builds. This needs to be an `Option` because
|
||||
/// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
|
||||
/// of the `remove_noop_landing_pads` and `abort_unwinding_calls` passes.
|
||||
unwind: Option<BasicBlock>,
|
||||
},
|
||||
|
||||
|
@ -2601,42 +2601,104 @@ where
|
||||
fn adjust_for_abi(&mut self, cx: &C, abi: SpecAbi);
|
||||
}
|
||||
|
||||
/// Calculates whether a function's ABI can unwind or not.
|
||||
///
|
||||
/// This takes two primary parameters:
|
||||
///
|
||||
/// * `codegen_fn_attr_flags` - these are flags calculated as part of the
|
||||
/// codegen attrs for a defined function. For function pointers this set of
|
||||
/// flags is the empty set. This is only applicable for Rust-defined
|
||||
/// functions, and generally isn't needed except for small optimizations where
|
||||
/// we try to say a function which otherwise might look like it could unwind
|
||||
/// doesn't actually unwind (such as for intrinsics and such).
|
||||
///
|
||||
/// * `abi` - this is the ABI that the function is defined with. This is the
|
||||
/// primary factor for determining whether a function can unwind or not.
|
||||
///
|
||||
/// Note that in this case unwinding is not necessarily panicking in Rust. Rust
|
||||
/// panics are implemented with unwinds on most platform (when
|
||||
/// `-Cpanic=unwind`), but this also accounts for `-Cpanic=abort` build modes.
|
||||
/// Notably unwinding is disallowed for more non-Rust ABIs unless it's
|
||||
/// specifically in the name (e.g. `"C-unwind"`). Unwinding within each ABI is
|
||||
/// defined for each ABI individually, but it always corresponds to some form of
|
||||
/// stack-based unwinding (the exact mechanism of which varies
|
||||
/// platform-by-platform).
|
||||
///
|
||||
/// Rust functions are classfied whether or not they can unwind based on the
|
||||
/// active "panic strategy". In other words Rust functions are considered to
|
||||
/// unwind in `-Cpanic=unwind` mode and cannot unwind in `-Cpanic=abort` mode.
|
||||
/// Note that Rust supports intermingling panic=abort and panic=unwind code, but
|
||||
/// only if the final panic mode is panic=abort. In this scenario any code
|
||||
/// previously compiled assuming that a function can unwind is still correct, it
|
||||
/// just never happens to actually unwind at runtime.
|
||||
///
|
||||
/// This function's answer to whether or not a function can unwind is quite
|
||||
/// impactful throughout the compiler. This affects things like:
|
||||
///
|
||||
/// * Calling a function which can't unwind means codegen simply ignores any
|
||||
/// associated unwinding cleanup.
|
||||
/// * Calling a function which can unwind from a function which can't unwind
|
||||
/// causes the `abort_unwinding_calls` MIR pass to insert a landing pad that
|
||||
/// aborts the process.
|
||||
/// * This affects whether functions have the LLVM `nounwind` attribute, which
|
||||
/// affects various optimizations and codegen.
|
||||
///
|
||||
/// FIXME: this is actually buggy with respect to Rust functions. Rust functions
|
||||
/// compiled with `-Cpanic=unwind` and referenced from another crate compiled
|
||||
/// with `-Cpanic=abort` will look like they can't unwind when in fact they
|
||||
/// might (from a foreign exception or similar).
|
||||
pub fn fn_can_unwind(
|
||||
panic_strategy: PanicStrategy,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
codegen_fn_attr_flags: CodegenFnAttrFlags,
|
||||
call_conv: Conv,
|
||||
abi: SpecAbi,
|
||||
) -> bool {
|
||||
if panic_strategy != PanicStrategy::Unwind {
|
||||
// In panic=abort mode we assume nothing can unwind anywhere, so
|
||||
// optimize based on this!
|
||||
false
|
||||
} else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::UNWIND) {
|
||||
// If a specific #[unwind] attribute is present, use that.
|
||||
true
|
||||
} else if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) {
|
||||
// Special attribute for allocator functions, which can't unwind.
|
||||
false
|
||||
} else {
|
||||
if call_conv == Conv::Rust {
|
||||
// Any Rust method (or `extern "Rust" fn` or `extern
|
||||
// "rust-call" fn`) is explicitly allowed to unwind
|
||||
// (unless it has no-unwind attribute, handled above).
|
||||
true
|
||||
} else {
|
||||
// Anything else is either:
|
||||
// Special attribute for functions which can't unwind.
|
||||
if codegen_fn_attr_flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise if this isn't special then unwinding is generally determined by
|
||||
// the ABI of the itself. ABIs like `C` have variants which also
|
||||
// specifically allow unwinding (`C-unwind`), but not all platform-specific
|
||||
// ABIs have such an option. Otherwise the only other thing here is Rust
|
||||
// itself, and those ABIs are determined by the panic strategy configured
|
||||
// for this compilation.
|
||||
//
|
||||
// 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
|
||||
// Unfortunately at this time there's also another caveat. Rust [RFC
|
||||
// 2945][rfc] has been accepted and is in the process of being implemented
|
||||
// and stabilized. In this interim state we need to deal with historical
|
||||
// rustc behavior as well as plan for future rustc behavior.
|
||||
//
|
||||
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
|
||||
// Historically functions declared with `extern "C"` were marked at the
|
||||
// codegen layer as `nounwind`. This happened regardless of `panic=unwind`
|
||||
// or not. This is UB for functions in `panic=unwind` mode that then
|
||||
// actually panic and unwind. Note that this behavior is true for both
|
||||
// externally declared functions as well as Rust-defined function.
|
||||
//
|
||||
// In both of these cases, we should refer to the ABI to determine whether or not we
|
||||
// should unwind. See Rust RFC 2945 for more information on this behavior, here:
|
||||
// https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
|
||||
// To fix this UB rustc would like to change in the future to catch unwinds
|
||||
// from function calls that may unwind within a Rust-defined `extern "C"`
|
||||
// function and forcibly abort the process, thereby respecting the
|
||||
// `nounwind` attribut emitted for `extern "C"`. This behavior change isn't
|
||||
// ready to roll out, so determining whether or not the `C` family of ABIs
|
||||
// unwinds is conditional not only on their definition but also whether the
|
||||
// `#![feature(c_unwind)]` feature gate is active.
|
||||
//
|
||||
// Note that this means that unlike historical compilers rustc now, by
|
||||
// default, unconditionally thinks that the `C` ABI may unwind. This will
|
||||
// prevent some optimization opportunities, however, so we try to scope this
|
||||
// change and only assume that `C` unwinds with `panic=unwind` (as opposed
|
||||
// to `panic=abort`).
|
||||
//
|
||||
// Eventually the check against `c_unwind` here will ideally get removed and
|
||||
// this'll be a little cleaner as it'll be a straightforward check of the
|
||||
// ABI.
|
||||
//
|
||||
// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
|
||||
use SpecAbi::*;
|
||||
match abi {
|
||||
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
|
||||
unwind
|
||||
|| (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
|
||||
}
|
||||
Cdecl
|
||||
| Fastcall
|
||||
@ -2656,10 +2718,7 @@ pub fn fn_can_unwind(
|
||||
| RustIntrinsic
|
||||
| PlatformIntrinsic
|
||||
| Unadjusted => false,
|
||||
// In the `if` above, we checked for functions with the Rust calling convention.
|
||||
Rust | RustCall => unreachable!(),
|
||||
}
|
||||
}
|
||||
Rust | RustCall => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2695,11 +2754,6 @@ pub fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi) -> Conv {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fn_ptr_codegen_fn_attr_flags() -> CodegenFnAttrFlags {
|
||||
// Assume that fn pointers may always unwind
|
||||
CodegenFnAttrFlags::UNWIND
|
||||
}
|
||||
|
||||
impl<'tcx, C> FnAbiExt<'tcx, C> for call::FnAbi<'tcx, Ty<'tcx>>
|
||||
where
|
||||
C: LayoutOf<Ty = Ty<'tcx>, TyAndLayout = TyAndLayout<'tcx>>
|
||||
@ -2709,7 +2763,7 @@ where
|
||||
+ HasParamEnv<'tcx>,
|
||||
{
|
||||
fn of_fn_ptr(cx: &C, sig: ty::PolyFnSig<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
|
||||
call::FnAbi::new_internal(cx, sig, extra_args, None, fn_ptr_codegen_fn_attr_flags(), false)
|
||||
call::FnAbi::new_internal(cx, sig, extra_args, None, CodegenFnAttrFlags::empty(), false)
|
||||
}
|
||||
|
||||
fn of_instance(cx: &C, instance: ty::Instance<'tcx>, extra_args: &[Ty<'tcx>]) -> Self {
|
||||
@ -2901,12 +2955,7 @@ where
|
||||
c_variadic: sig.c_variadic,
|
||||
fixed_count: inputs.len(),
|
||||
conv,
|
||||
can_unwind: fn_can_unwind(
|
||||
cx.tcx().sess.panic_strategy(),
|
||||
codegen_fn_attr_flags,
|
||||
conv,
|
||||
sig.abi,
|
||||
),
|
||||
can_unwind: fn_can_unwind(cx.tcx(), codegen_fn_attr_flags, sig.abi),
|
||||
};
|
||||
fn_abi.adjust_for_abi(cx, sig.abi);
|
||||
debug!("FnAbi::new_internal = {:?}", fn_abi);
|
||||
|
@ -18,12 +18,7 @@ use super::{
|
||||
|
||||
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
fn fn_can_unwind(&self, attrs: CodegenFnAttrFlags, abi: Abi) -> bool {
|
||||
layout::fn_can_unwind(
|
||||
self.tcx.sess.panic_strategy(),
|
||||
attrs,
|
||||
layout::conv_from_spec_abi(*self.tcx, abi),
|
||||
abi,
|
||||
)
|
||||
layout::fn_can_unwind(*self.tcx, attrs, abi)
|
||||
}
|
||||
|
||||
pub(super) fn eval_terminator(
|
||||
@ -77,7 +72,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
(
|
||||
fn_val,
|
||||
caller_abi,
|
||||
self.fn_can_unwind(layout::fn_ptr_codegen_fn_attr_flags(), caller_abi),
|
||||
self.fn_can_unwind(CodegenFnAttrFlags::empty(), caller_abi),
|
||||
)
|
||||
}
|
||||
ty::FnDef(def_id, substs) => {
|
||||
|
@ -16,7 +16,7 @@ use std::fmt;
|
||||
use std::iter;
|
||||
|
||||
use crate::transform::{
|
||||
add_call_guards, add_moves_for_packed_drops, no_landing_pads, remove_noop_landing_pads,
|
||||
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, remove_noop_landing_pads,
|
||||
run_passes, simplify,
|
||||
};
|
||||
use crate::util::elaborate_drops::{self, DropElaborator, DropFlagMode, DropStyle};
|
||||
@ -81,10 +81,10 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
|
||||
MirPhase::Const,
|
||||
&[&[
|
||||
&add_moves_for_packed_drops::AddMovesForPackedDrops,
|
||||
&no_landing_pads::NoLandingPads,
|
||||
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
||||
&simplify::SimplifyCfg::new("make_shim"),
|
||||
&add_call_guards::CriticalCallEdges,
|
||||
&abort_unwinding_calls::AbortUnwindingCalls,
|
||||
]],
|
||||
);
|
||||
|
||||
|
141
compiler/rustc_mir/src/transform/abort_unwinding_calls.rs
Normal file
141
compiler/rustc_mir/src/transform/abort_unwinding_calls.rs
Normal file
@ -0,0 +1,141 @@
|
||||
use crate::transform::MirPass;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::layout;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
/// A pass that runs which is targeted at ensuring that codegen guarantees about
|
||||
/// unwinding are upheld for compilations of panic=abort programs.
|
||||
///
|
||||
/// When compiling with panic=abort codegen backends generally want to assume
|
||||
/// that all Rust-defined functions do not unwind, and it's UB if they actually
|
||||
/// do unwind. Foreign functions, however, can be declared as "may unwind" via
|
||||
/// their ABI (e.g. `extern "C-unwind"`). To uphold the guarantees that
|
||||
/// Rust-defined functions never unwind a well-behaved Rust program needs to
|
||||
/// catch unwinding from foreign functions and force them to abort.
|
||||
///
|
||||
/// This pass walks over all functions calls which may possibly unwind,
|
||||
/// and if any are found sets their cleanup to a block that aborts the process.
|
||||
/// This forces all unwinds, in panic=abort mode happening in foreign code, to
|
||||
/// trigger a process abort.
|
||||
#[derive(PartialEq)]
|
||||
pub struct AbortUnwindingCalls;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
let def_id = body.source.def_id();
|
||||
let kind = tcx.def_kind(def_id);
|
||||
|
||||
// We don't simplify the MIR of constants at this time because that
|
||||
// namely results in a cyclic query when we call `tcx.type_of` below.
|
||||
let is_function = match kind {
|
||||
DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) => true,
|
||||
_ => tcx.is_closure(def_id),
|
||||
};
|
||||
if !is_function {
|
||||
return;
|
||||
}
|
||||
|
||||
// This pass only runs on functions which themselves cannot unwind,
|
||||
// forcibly changing the body of the function to structurally provide
|
||||
// this guarantee by aborting on an unwind. If this function can unwind,
|
||||
// then there's nothing to do because it already should work correctly.
|
||||
//
|
||||
// Here we test for this function itself whether its ABI allows
|
||||
// unwinding or not.
|
||||
let body_flags = tcx.codegen_fn_attrs(def_id).flags;
|
||||
let body_ty = tcx.type_of(def_id);
|
||||
let body_abi = match body_ty.kind() {
|
||||
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
|
||||
ty::Closure(..) => Abi::RustCall,
|
||||
ty::Generator(..) => Abi::Rust,
|
||||
_ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty),
|
||||
};
|
||||
let body_can_unwind = layout::fn_can_unwind(tcx, body_flags, body_abi);
|
||||
|
||||
// Look in this function body for any basic blocks which are terminated
|
||||
// with a function call, and whose function we're calling may unwind.
|
||||
// This will filter to functions with `extern "C-unwind"` ABIs, for
|
||||
// example.
|
||||
let mut calls_to_terminate = Vec::new();
|
||||
let mut cleanups_to_remove = Vec::new();
|
||||
for (id, block) in body.basic_blocks().iter_enumerated() {
|
||||
if block.is_cleanup {
|
||||
continue;
|
||||
}
|
||||
let terminator = match &block.terminator {
|
||||
Some(terminator) => terminator,
|
||||
None => continue,
|
||||
};
|
||||
let span = terminator.source_info.span;
|
||||
|
||||
let call_can_unwind = match &terminator.kind {
|
||||
TerminatorKind::Call { func, .. } => {
|
||||
let ty = func.ty(body, tcx);
|
||||
let sig = ty.fn_sig(tcx);
|
||||
let flags = match ty.kind() {
|
||||
ty::FnPtr(_) => CodegenFnAttrFlags::empty(),
|
||||
ty::FnDef(def_id, _) => tcx.codegen_fn_attrs(*def_id).flags,
|
||||
_ => span_bug!(span, "invalid callee of type {:?}", ty),
|
||||
};
|
||||
layout::fn_can_unwind(tcx, flags, sig.abi())
|
||||
}
|
||||
TerminatorKind::Drop { .. }
|
||||
| TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::Assert { .. }
|
||||
| TerminatorKind::FalseUnwind { .. } => {
|
||||
layout::fn_can_unwind(tcx, CodegenFnAttrFlags::empty(), Abi::Rust)
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
// If this function call can't unwind, then there's no need for it
|
||||
// to have a landing pad. This means that we can remove any cleanup
|
||||
// registered for it.
|
||||
if !call_can_unwind {
|
||||
cleanups_to_remove.push(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise if this function can unwind, then if the outer function
|
||||
// can also unwind there's nothing to do. If the outer function
|
||||
// can't unwind, however, we need to change the landing pad for this
|
||||
// function call to one that aborts.
|
||||
if !body_can_unwind {
|
||||
calls_to_terminate.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// For call instructions which need to be terminated, we insert a
|
||||
// singular basic block which simply terminates, and then configure the
|
||||
// `cleanup` attribute for all calls we found to this basic block we
|
||||
// insert which means that any unwinding that happens in the functions
|
||||
// will force an abort of the process.
|
||||
if !calls_to_terminate.is_empty() {
|
||||
let bb = BasicBlockData {
|
||||
statements: Vec::new(),
|
||||
is_cleanup: true,
|
||||
terminator: Some(Terminator {
|
||||
source_info: SourceInfo::outermost(body.span),
|
||||
kind: TerminatorKind::Abort,
|
||||
}),
|
||||
};
|
||||
let abort_bb = body.basic_blocks_mut().push(bb);
|
||||
|
||||
for bb in calls_to_terminate {
|
||||
let cleanup = body.basic_blocks_mut()[bb].terminator_mut().unwind_mut().unwrap();
|
||||
*cleanup = Some(abort_bb);
|
||||
}
|
||||
}
|
||||
|
||||
for id in cleanups_to_remove {
|
||||
let cleanup = body.basic_blocks_mut()[id].terminator_mut().unwind_mut().unwrap();
|
||||
*cleanup = None;
|
||||
}
|
||||
|
||||
// We may have invalidated some `cleanup` blocks so clean those up now.
|
||||
super::simplify::remove_dead_blocks(tcx, body);
|
||||
}
|
||||
}
|
@ -53,7 +53,6 @@ use crate::dataflow::impls::{
|
||||
MaybeBorrowedLocals, MaybeLiveLocals, MaybeRequiresStorage, MaybeStorageLive,
|
||||
};
|
||||
use crate::dataflow::{self, Analysis};
|
||||
use crate::transform::no_landing_pads::no_landing_pads;
|
||||
use crate::transform::simplify;
|
||||
use crate::transform::MirPass;
|
||||
use crate::util::dump_mir;
|
||||
@ -960,8 +959,6 @@ fn create_generator_drop_shim<'tcx>(
|
||||
)
|
||||
}
|
||||
|
||||
no_landing_pads(tcx, &mut body);
|
||||
|
||||
// Make sure we remove dead blocks to remove
|
||||
// unrelated code from the resume part of the function
|
||||
simplify::remove_dead_blocks(tcx, &mut body);
|
||||
@ -1133,8 +1130,6 @@ fn create_generator_resume_function<'tcx>(
|
||||
make_generator_state_argument_indirect(tcx, body);
|
||||
make_generator_state_argument_pinned(tcx, body);
|
||||
|
||||
no_landing_pads(tcx, body);
|
||||
|
||||
// Make sure we remove dead blocks to remove
|
||||
// unrelated code from the drop part of the function
|
||||
simplify::remove_dead_blocks(tcx, body);
|
||||
|
@ -13,6 +13,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub mod abort_unwinding_calls;
|
||||
pub mod add_call_guards;
|
||||
pub mod add_moves_for_packed_drops;
|
||||
pub mod add_retag;
|
||||
@ -39,7 +40,6 @@ pub mod lower_intrinsics;
|
||||
pub mod lower_slice_len;
|
||||
pub mod match_branches;
|
||||
pub mod multiple_return_terminators;
|
||||
pub mod no_landing_pads;
|
||||
pub mod nrvo;
|
||||
pub mod promote_consts;
|
||||
pub mod remove_noop_landing_pads;
|
||||
@ -451,7 +451,6 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
|
||||
|
||||
let post_borrowck_cleanup: &[&dyn MirPass<'tcx>] = &[
|
||||
// Remove all things only needed by analysis
|
||||
&no_landing_pads::NoLandingPads,
|
||||
&simplify_branches::SimplifyBranches::new("initial"),
|
||||
&remove_noop_landing_pads::RemoveNoopLandingPads,
|
||||
&cleanup_post_borrowck::CleanupNonCodegenStatements,
|
||||
@ -459,7 +458,10 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
|
||||
// These next passes must be executed together
|
||||
&add_call_guards::CriticalCallEdges,
|
||||
&elaborate_drops::ElaborateDrops,
|
||||
&no_landing_pads::NoLandingPads,
|
||||
// This will remove extraneous landing pads which are no longer
|
||||
// necessary as well as well as forcing any call in a non-unwinding
|
||||
// function calling a possibly-unwinding function to abort the process.
|
||||
&abort_unwinding_calls::AbortUnwindingCalls,
|
||||
// AddMovesForPackedDrops needs to run after drop
|
||||
// elaboration.
|
||||
&add_moves_for_packed_drops::AddMovesForPackedDrops,
|
||||
|
@ -1,28 +0,0 @@
|
||||
//! This pass removes the unwind branch of all the terminators when the no-landing-pads option is
|
||||
//! specified.
|
||||
|
||||
use crate::transform::MirPass;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
pub struct NoLandingPads;
|
||||
|
||||
impl<'tcx> MirPass<'tcx> for NoLandingPads {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
no_landing_pads(tcx, body)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no_landing_pads<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
if tcx.sess.panic_strategy() != PanicStrategy::Abort {
|
||||
return;
|
||||
}
|
||||
|
||||
for block in body.basic_blocks_mut() {
|
||||
let terminator = block.terminator_mut();
|
||||
if let Some(unwind) = terminator.kind.unwind_mut() {
|
||||
unwind.take();
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ use crate::build;
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::scope::DropKind;
|
||||
use crate::thir::pattern::pat_from_hir;
|
||||
use rustc_attr::{self as attr, UnwindAttr};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
@ -19,7 +18,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
|
||||
use rustc_span::symbol::{kw, sym};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
use rustc_target::spec::PanicStrategy;
|
||||
|
||||
use super::lints;
|
||||
|
||||
@ -581,60 +579,6 @@ macro_rules! unpack {
|
||||
}};
|
||||
}
|
||||
|
||||
fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
|
||||
// Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
|
||||
let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
|
||||
let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
|
||||
|
||||
// We never unwind, so it's not relevant to stop an unwind.
|
||||
if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
|
||||
return false;
|
||||
}
|
||||
|
||||
match unwind_attr {
|
||||
// If an `#[unwind]` attribute was found, we should adhere to it.
|
||||
Some(UnwindAttr::Allowed) => false,
|
||||
Some(UnwindAttr::Aborts) => true,
|
||||
// If no attribute was found and the panic strategy is `unwind`, then we should examine
|
||||
// the function's ABI string to determine whether it should abort upon panic.
|
||||
None if tcx.features().c_unwind => {
|
||||
use Abi::*;
|
||||
match abi {
|
||||
// In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
|
||||
// permits unwinding. If so, we should not abort. Otherwise, we should.
|
||||
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
|
||||
!unwind
|
||||
}
|
||||
// Rust and `rust-call` functions are allowed to unwind, and should not abort.
|
||||
Rust | RustCall => false,
|
||||
// Other ABI's should abort.
|
||||
Cdecl
|
||||
| Fastcall
|
||||
| Vectorcall
|
||||
| Aapcs
|
||||
| Win64
|
||||
| SysV64
|
||||
| PtxKernel
|
||||
| Msp430Interrupt
|
||||
| X86Interrupt
|
||||
| AmdGpuKernel
|
||||
| EfiApi
|
||||
| AvrInterrupt
|
||||
| AvrNonBlockingInterrupt
|
||||
| CCmseNonSecureCall
|
||||
| Wasm
|
||||
| RustIntrinsic
|
||||
| PlatformIntrinsic
|
||||
| Unadjusted => true,
|
||||
}
|
||||
}
|
||||
// If the `c_unwind` feature gate is not active, follow the behavior that was in place
|
||||
// prior to #76570. This is a special case: some functions have a C ABI but are meant to
|
||||
// unwind anyway. Don't stop them.
|
||||
None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// the main entry point for building MIR for a function
|
||||
|
||||
@ -704,8 +648,7 @@ where
|
||||
}));
|
||||
let source_info = builder.source_info(fn_end);
|
||||
builder.cfg.terminate(return_block, source_info, TerminatorKind::Return);
|
||||
let should_abort = should_abort_on_panic(tcx, fn_def.did, abi);
|
||||
builder.build_drop_trees(should_abort);
|
||||
builder.build_drop_trees();
|
||||
return_block.unit()
|
||||
}));
|
||||
|
||||
@ -752,7 +695,7 @@ fn construct_const<'a, 'tcx>(
|
||||
let source_info = builder.source_info(span);
|
||||
builder.cfg.terminate(block, source_info, TerminatorKind::Return);
|
||||
|
||||
builder.build_drop_trees(false);
|
||||
builder.build_drop_trees();
|
||||
|
||||
builder.finish()
|
||||
}
|
||||
|
@ -1249,21 +1249,20 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Build the unwind and generator drop trees.
|
||||
crate fn build_drop_trees(&mut self, should_abort: bool) {
|
||||
crate fn build_drop_trees(&mut self) {
|
||||
if self.generator_kind.is_some() {
|
||||
self.build_generator_drop_trees(should_abort);
|
||||
self.build_generator_drop_trees();
|
||||
} else {
|
||||
Self::build_unwind_tree(
|
||||
&mut self.cfg,
|
||||
&mut self.scopes.unwind_drops,
|
||||
self.fn_span,
|
||||
should_abort,
|
||||
&mut None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_generator_drop_trees(&mut self, should_abort: bool) {
|
||||
fn build_generator_drop_trees(&mut self) {
|
||||
// Build the drop tree for dropping the generator while it's suspended.
|
||||
let drops = &mut self.scopes.generator_drops;
|
||||
let cfg = &mut self.cfg;
|
||||
@ -1281,7 +1280,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
||||
// Build the drop tree for unwinding in the normal control flow paths.
|
||||
let resume_block = &mut None;
|
||||
let unwind_drops = &mut self.scopes.unwind_drops;
|
||||
Self::build_unwind_tree(cfg, unwind_drops, fn_span, should_abort, resume_block);
|
||||
Self::build_unwind_tree(cfg, unwind_drops, fn_span, resume_block);
|
||||
|
||||
// Build the drop tree for unwinding when dropping a suspended
|
||||
// generator.
|
||||
@ -1296,26 +1295,20 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
|
||||
drops.entry_points.push((drop_data.1, blocks[drop_idx].unwrap()));
|
||||
}
|
||||
}
|
||||
Self::build_unwind_tree(cfg, drops, fn_span, should_abort, resume_block);
|
||||
Self::build_unwind_tree(cfg, drops, fn_span, resume_block);
|
||||
}
|
||||
|
||||
fn build_unwind_tree(
|
||||
cfg: &mut CFG<'tcx>,
|
||||
drops: &mut DropTree,
|
||||
fn_span: Span,
|
||||
should_abort: bool,
|
||||
resume_block: &mut Option<BasicBlock>,
|
||||
) {
|
||||
let mut blocks = IndexVec::from_elem(None, &drops.drops);
|
||||
blocks[ROOT_NODE] = *resume_block;
|
||||
drops.build_mir::<Unwind>(cfg, &mut blocks);
|
||||
if let (None, Some(resume)) = (*resume_block, blocks[ROOT_NODE]) {
|
||||
// `TerminatorKind::Abort` is used for `#[unwind(aborts)]`
|
||||
// functions.
|
||||
let terminator =
|
||||
if should_abort { TerminatorKind::Abort } else { TerminatorKind::Resume };
|
||||
|
||||
cfg.terminate(resume, SourceInfo::outermost(fn_span), terminator);
|
||||
cfg.terminate(resume, SourceInfo::outermost(fn_span), TerminatorKind::Resume);
|
||||
|
||||
*resume_block = blocks[ROOT_NODE];
|
||||
}
|
||||
|
@ -2776,8 +2776,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
|
||||
} else if tcx.sess.check_name(attr, sym::rustc_allocator) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
|
||||
} else if tcx.sess.check_name(attr, sym::unwind) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
|
||||
} else if tcx.sess.check_name(attr, sym::ffi_returns_twice) {
|
||||
if tcx.is_foreign_item(id) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
|
||||
@ -2829,7 +2827,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
.emit();
|
||||
}
|
||||
} else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||
} else if tcx.sess.check_name(attr, sym::naked) {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
|
||||
} else if tcx.sess.check_name(attr, sym::no_mangle) {
|
||||
@ -3200,6 +3198,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
|
||||
}
|
||||
|
||||
// Any linkage to LLVM intrinsics for now forcibly marks them all as never
|
||||
// unwinds since LLVM sometimes can't handle codegen which `invoke`s
|
||||
// intrinsic functions.
|
||||
if let Some(name) = &codegen_fn_attrs.link_name {
|
||||
if name.as_str().starts_with("llvm.") {
|
||||
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
|
||||
}
|
||||
}
|
||||
|
||||
codegen_fn_attrs
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,6 @@
|
||||
#![feature(try_blocks)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unsized_fn_params)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(variant_count)]
|
||||
#![feature(tbm_target_feature)]
|
||||
#![feature(sse4a_target_feature)]
|
||||
|
@ -20,11 +20,11 @@
|
||||
#![feature(panic_unwind)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(std_internals)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(abi_thiscall)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![panic_runtime]
|
||||
#![feature(panic_runtime)]
|
||||
#![feature(c_unwind)]
|
||||
// `real_imp` is unused with Miri, so silence warnings.
|
||||
#![cfg_attr(miri, allow(dead_code))]
|
||||
|
||||
@ -98,8 +98,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any
|
||||
// Entry point for raising an exception, just delegates to the platform-specific
|
||||
// implementation.
|
||||
#[rustc_std_internal_symbol]
|
||||
#[unwind(allowed)]
|
||||
pub unsafe extern "C" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 {
|
||||
pub unsafe extern "C-unwind" fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 {
|
||||
let payload = Box::from_raw((*payload).take_box());
|
||||
|
||||
imp::panic(payload)
|
||||
|
@ -233,15 +233,14 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
|
||||
// support capturing exceptions with std::exception_ptr, which we can't support
|
||||
// because Box<dyn Any> isn't clonable.
|
||||
macro_rules! define_cleanup {
|
||||
($abi:tt) => {
|
||||
($abi:tt $abi2:tt) => {
|
||||
unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
|
||||
if let Exception { data: Some(b) } = e.read() {
|
||||
drop(b);
|
||||
super::__rust_drop_panic();
|
||||
}
|
||||
}
|
||||
#[unwind(allowed)]
|
||||
unsafe extern $abi fn exception_copy(_dest: *mut Exception,
|
||||
unsafe extern $abi2 fn exception_copy(_dest: *mut Exception,
|
||||
_src: *mut Exception)
|
||||
-> *mut Exception {
|
||||
panic!("Rust panics cannot be copied");
|
||||
@ -250,9 +249,9 @@ macro_rules! define_cleanup {
|
||||
}
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(target_arch = "x86")] {
|
||||
define_cleanup!("thiscall");
|
||||
define_cleanup!("thiscall" "thiscall-unwind");
|
||||
} else {
|
||||
define_cleanup!("C");
|
||||
define_cleanup!("C" "C-unwind");
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,8 +306,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
|
||||
ptr!(exception_copy) as u32,
|
||||
);
|
||||
|
||||
extern "system" {
|
||||
#[unwind(allowed)]
|
||||
extern "system-unwind" {
|
||||
fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,7 @@
|
||||
#![feature(auto_traits)]
|
||||
#![feature(bench_black_box)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(c_unwind)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(cfg_accessible)]
|
||||
#![feature(cfg_eval)]
|
||||
@ -327,7 +328,6 @@
|
||||
#![feature(try_reserve)]
|
||||
#![feature(unboxed_closures)]
|
||||
#![feature(unsafe_cell_raw_get)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(vec_into_raw_parts)]
|
||||
#![feature(vec_spare_capacity)]
|
||||
|
@ -43,11 +43,13 @@ use realstd::io::set_output_capture;
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C" {
|
||||
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
|
||||
}
|
||||
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C-unwind" {
|
||||
/// `payload` is passed through another layer of raw pointers as `&mut dyn Trait` is not
|
||||
/// FFI-safe. `BoxMeUp` lazily performs allocation only when needed (this avoids allocations
|
||||
/// when using the "abort" panic runtime).
|
||||
#[unwind(allowed)]
|
||||
fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32;
|
||||
}
|
||||
|
||||
@ -460,7 +462,6 @@ pub fn begin_panic_fmt(msg: &fmt::Arguments<'_>) -> ! {
|
||||
|
||||
/// Entry point of panics from the libcore crate (`panic_impl` lang item).
|
||||
#[cfg_attr(not(test), panic_handler)]
|
||||
#[unwind(allowed)]
|
||||
pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
|
||||
struct PanicPayload<'a> {
|
||||
inner: &'a fmt::Arguments<'a>,
|
||||
|
@ -3,8 +3,8 @@
|
||||
#![feature(link_cfg)]
|
||||
#![feature(nll)]
|
||||
#![feature(staged_api)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(static_nobundle)]
|
||||
#![feature(c_unwind)]
|
||||
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
|
@ -81,9 +81,10 @@ pub type _Unwind_Exception_Cleanup_Fn =
|
||||
all(feature = "llvm-libunwind", any(target_os = "fuchsia", target_os = "linux")),
|
||||
link(name = "unwind", kind = "static")
|
||||
)]
|
||||
extern "C" {
|
||||
#[unwind(allowed)]
|
||||
extern "C-unwind" {
|
||||
pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
|
||||
pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
|
||||
pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
|
||||
@ -230,9 +231,10 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
|
||||
#[cfg_attr(all(feature = "llvm-libunwind",
|
||||
any(target_os = "fuchsia", target_os = "linux")),
|
||||
link(name = "unwind", kind = "static"))]
|
||||
extern "C" {
|
||||
#[unwind(allowed)]
|
||||
extern "C-unwind" {
|
||||
pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
|
||||
trace_argument: *mut c_void)
|
||||
-> _Unwind_Reason_Code;
|
||||
@ -242,8 +244,7 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
|
||||
#[cfg_attr(all(feature = "llvm-libunwind",
|
||||
any(target_os = "fuchsia", target_os = "linux")),
|
||||
link(name = "unwind", kind = "static"))]
|
||||
extern "C" {
|
||||
#[unwind(allowed)]
|
||||
extern "C-unwind" {
|
||||
pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
#![no_std]
|
||||
use core::ffi::VaList;
|
||||
|
||||
@ -13,7 +13,6 @@ extern "C" {
|
||||
fn foreign_c_variadic_1(_: VaList, ...);
|
||||
}
|
||||
|
||||
#[unwind(aborts)] // FIXME(#58794)
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_0() {
|
||||
// Ensure that we correctly call foreign C-variadic functions.
|
||||
// CHECK: call void (i32, ...) @foreign_c_variadic_0([[PARAM:i32( signext)?]] 0)
|
||||
@ -28,24 +27,20 @@ pub unsafe extern "C" fn use_foreign_c_variadic_0() {
|
||||
|
||||
// Ensure that we do not remove the `va_list` passed to the foreign function when
|
||||
// removing the "spoofed" `VaListImpl` that is used by Rust defined C-variadics.
|
||||
#[unwind(aborts)] // FIXME(#58794)
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_1_0(ap: VaList) {
|
||||
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap)
|
||||
foreign_c_variadic_1(ap);
|
||||
}
|
||||
|
||||
#[unwind(aborts)] // FIXME(#58794)
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_1_1(ap: VaList) {
|
||||
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 42)
|
||||
foreign_c_variadic_1(ap, 42i32);
|
||||
}
|
||||
#[unwind(aborts)] // FIXME(#58794)
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_1_2(ap: VaList) {
|
||||
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42)
|
||||
foreign_c_variadic_1(ap, 2i32, 42i32);
|
||||
}
|
||||
|
||||
#[unwind(aborts)] // FIXME(#58794)
|
||||
pub unsafe extern "C" fn use_foreign_c_variadic_1_3(ap: VaList) {
|
||||
// CHECK: call void ({{.*}}*, ...) @foreign_c_variadic_1({{.*}} %ap, [[PARAM]] 2, [[PARAM]] 42, [[PARAM]] 0)
|
||||
foreign_c_variadic_1(ap, 2i32, 42i32, 0i32);
|
||||
|
@ -10,6 +10,7 @@
|
||||
// ignore-riscv64 FIXME
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
extern "C" {
|
||||
fn bar();
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
// We need a function which is normally called through the PLT.
|
||||
extern "C" {
|
||||
// CHECK: Function Attrs: nounwind nonlazybind
|
||||
// CHECK: Function Attrs:{{.*}}nonlazybind
|
||||
fn getenv(name: *const u8) -> *mut u8;
|
||||
}
|
||||
|
||||
|
@ -1,20 +0,0 @@
|
||||
// compile-flags: -C panic=abort -O
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(unwind_attributes, core_intrinsics)]
|
||||
|
||||
extern "C" {
|
||||
#[unwind(allow)]
|
||||
fn bar(data: *mut u8);
|
||||
}
|
||||
extern "Rust" {
|
||||
fn catch(data: *mut u8, exception: *mut u8);
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @foo
|
||||
#[no_mangle]
|
||||
pub unsafe fn foo() -> i32 {
|
||||
// CHECK: call void @bar
|
||||
// CHECK: ret i32 0
|
||||
std::intrinsics::r#try(|x| bar(x), 0 as *mut u8, |x, y| catch(x, y))
|
||||
}
|
@ -1,18 +1,22 @@
|
||||
// compile-flags: -C panic=abort -C opt-level=0
|
||||
// compile-flags: -C panic=abort
|
||||
|
||||
// Test that `nounwind` atributes are applied to `C-unwind` extern functions when the
|
||||
// code is compiled with `panic=abort`. We disable optimizations above to prevent LLVM from
|
||||
// inferring the attribute.
|
||||
// Test that `nounwind` atributes are not applied to `C-unwind` extern functions
|
||||
// even when the code is compiled with `panic=abort`.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
// CHECK: @rust_item_that_can_unwind() unnamed_addr #0 {
|
||||
extern "C-unwind" {
|
||||
fn may_unwind();
|
||||
}
|
||||
|
||||
// CHECK: @rust_item_that_can_unwind() unnamed_addr #0
|
||||
#[no_mangle]
|
||||
pub extern "C-unwind" fn rust_item_that_can_unwind() {
|
||||
pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
|
||||
may_unwind();
|
||||
}
|
||||
|
||||
// Now, make sure that the LLVM attributes for this functions are correct. First, make
|
||||
// sure that the first item is correctly marked with the `nounwind` attribute:
|
||||
//
|
||||
// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
|
||||
// CHECK-NOT: attributes #0 = { {{.*}}nounwind{{.*}} }
|
||||
|
@ -0,0 +1,16 @@
|
||||
// compile-flags: -C opt-level=0 -Cpanic=abort
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// We disable optimizations to prevent LLVM from infering the attribute.
|
||||
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: @foo
|
||||
#[no_mangle]
|
||||
pub extern "C" fn foo() {}
|
||||
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: @bar
|
||||
#[no_mangle]
|
||||
pub fn bar() {}
|
@ -0,0 +1,17 @@
|
||||
// compile-flags: -C opt-level=0
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
// We disable optimizations to prevent LLVM from infering the attribute.
|
||||
|
||||
extern "C" {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
// CHECK-NOT: Function Attrs:{{.*}}nounwind
|
||||
pub unsafe extern "C" fn foo() {
|
||||
bar();
|
||||
}
|
||||
|
||||
// Note that this test will get removed when `C-unwind` is fully stabilized
|
17
src/test/codegen/unwind-abis/nounwind.rs
Normal file
17
src/test/codegen/unwind-abis/nounwind.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// compile-flags: -C opt-level=0 -Cpanic=abort
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
// We disable optimizations to prevent LLVM from infering the attribute.
|
||||
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: @foo
|
||||
#[no_mangle]
|
||||
pub extern "C" fn foo() {}
|
||||
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: @bar
|
||||
#[no_mangle]
|
||||
pub fn bar() {}
|
16
src/test/codegen/unwind-and-panic-abort.rs
Normal file
16
src/test/codegen/unwind-and-panic-abort.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// compile-flags: -C panic=abort
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
extern "C-unwind" {
|
||||
fn bar();
|
||||
}
|
||||
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: define{{.*}}void @foo
|
||||
// CHECK: call void @llvm.trap()
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn foo() {
|
||||
bar();
|
||||
}
|
@ -2,19 +2,15 @@
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
// Make sure these all do *not* get the attribute.
|
||||
// We disable optimizations to prevent LLVM from infering the attribute.
|
||||
// CHECK-NOT: nounwind
|
||||
|
||||
// "C" ABI
|
||||
// pub extern fn foo() {} // FIXME right now we don't abort-on-panic but add `nounwind` nevertheless
|
||||
#[unwind(allowed)]
|
||||
pub extern "C" fn foo_allowed() {}
|
||||
pub extern "C-unwind" fn foo_unwind() {}
|
||||
|
||||
// "Rust"
|
||||
// (`extern "Rust"` could be removed as all `fn` get it implicitly; we leave it in for clarity.)
|
||||
pub extern "Rust" fn bar() {}
|
||||
#[unwind(allowed)]
|
||||
pub extern "Rust" fn bar_allowed() {}
|
||||
pub fn bar() {}
|
||||
|
@ -2,41 +2,21 @@
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
extern "C" {
|
||||
// CHECK: Function Attrs:{{.*}}nounwind
|
||||
// CHECK-NEXT: declare{{.*}}void @extern_fn
|
||||
fn extern_fn();
|
||||
// CHECK-NOT: Function Attrs:{{.*}}nounwind
|
||||
// CHECK: declare{{.*}}void @unwinding_extern_fn
|
||||
#[unwind(allowed)]
|
||||
fn unwinding_extern_fn();
|
||||
// CHECK-NOT: nounwind
|
||||
// CHECK: declare{{.*}}void @aborting_extern_fn
|
||||
#[unwind(aborts)]
|
||||
fn aborting_extern_fn(); // FIXME: we want to have the attribute here
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
extern "C-unwind" {
|
||||
// CHECK-NOT: nounwind
|
||||
// CHECK: declare{{.*}}void @rust_extern_fn
|
||||
fn rust_extern_fn();
|
||||
// CHECK-NOT: nounwind
|
||||
// CHECK: declare{{.*}}void @rust_unwinding_extern_fn
|
||||
#[unwind(allowed)]
|
||||
fn rust_unwinding_extern_fn();
|
||||
// CHECK-NOT: nounwind
|
||||
// CHECK: declare{{.*}}void @rust_aborting_extern_fn
|
||||
#[unwind(aborts)]
|
||||
fn rust_aborting_extern_fn(); // FIXME: we want to have the attribute here
|
||||
// CHECK: declare{{.*}}void @c_unwind_extern_fn
|
||||
fn c_unwind_extern_fn();
|
||||
}
|
||||
|
||||
pub unsafe fn force_declare() {
|
||||
extern_fn();
|
||||
unwinding_extern_fn();
|
||||
aborting_extern_fn();
|
||||
rust_extern_fn();
|
||||
rust_unwinding_extern_fn();
|
||||
rust_aborting_extern_fn();
|
||||
c_unwind_extern_fn();
|
||||
}
|
||||
|
@ -2,46 +2,47 @@
|
||||
+ // MIR for `main` after SimplifyCfg-early-opt
|
||||
|
||||
fn main() -> () {
|
||||
let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11
|
||||
let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
|
||||
let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
|
||||
let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:7:11: 7:11
|
||||
let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
|
||||
let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
|
||||
|
||||
bb0: {
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
- }
|
||||
-
|
||||
- bb1: {
|
||||
StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
- _2 = bar() -> bb2; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
+ _2 = bar() -> bb1; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
- _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
+ _2 = bar() -> [return: bb1, unwind: bb4]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
// mir::Constant
|
||||
// + span: $DIR/simplify_cfg.rs:7:12: 7:15
|
||||
// + span: $DIR/simplify_cfg.rs:9:12: 9:15
|
||||
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
|
||||
}
|
||||
|
||||
- bb2: {
|
||||
- switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
- switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
+ bb1: {
|
||||
+ switchInt(move _2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
+ switchInt(move _2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
}
|
||||
|
||||
- bb3: {
|
||||
+ bb2: {
|
||||
_0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
|
||||
return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
|
||||
_0 = const (); // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
|
||||
return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
|
||||
}
|
||||
|
||||
- bb4: {
|
||||
+ bb3: {
|
||||
_1 = const (); // scope 0 at $DIR/simplify_cfg.rs:9:10: 9:10
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
|
||||
goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
- }
|
||||
-
|
||||
_1 = const (); // scope 0 at $DIR/simplify_cfg.rs:11:10: 11:10
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
|
||||
goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
}
|
||||
|
||||
- bb5 (cleanup): {
|
||||
- resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
|
||||
+ bb4 (cleanup): {
|
||||
resume; // scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,71 +2,71 @@
|
||||
+ // MIR for `main` after SimplifyCfg-initial
|
||||
|
||||
fn main() -> () {
|
||||
let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:5:11: 5:11
|
||||
let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
|
||||
let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
|
||||
let mut _0: (); // return place in scope 0 at $DIR/simplify_cfg.rs:7:11: 7:11
|
||||
let mut _1: (); // in scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
|
||||
let mut _2: bool; // in scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
let mut _3: !; // in scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
|
||||
|
||||
bb0: {
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
+ falseUnwind -> [real: bb1, cleanup: bb5]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
+ falseUnwind -> [real: bb1, cleanup: bb5]; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
}
|
||||
|
||||
bb1: {
|
||||
- falseUnwind -> [real: bb2, cleanup: bb10]; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
- falseUnwind -> [real: bb2, cleanup: bb10]; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
- }
|
||||
-
|
||||
- bb2: {
|
||||
StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
- _2 = bar() -> [return: bb3, unwind: bb10]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
+ _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:7:12: 7:17
|
||||
StorageLive(_2); // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
- _2 = bar() -> [return: bb3, unwind: bb10]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
+ _2 = bar() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/simplify_cfg.rs:9:12: 9:17
|
||||
// mir::Constant
|
||||
// + span: $DIR/simplify_cfg.rs:7:12: 7:15
|
||||
// + span: $DIR/simplify_cfg.rs:9:12: 9:15
|
||||
// + literal: Const { ty: fn() -> bool {bar}, val: Value(Scalar(<ZST>)) }
|
||||
}
|
||||
|
||||
- bb3: {
|
||||
- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
- switchInt(move _2) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
+ bb2: {
|
||||
+ switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
+ switchInt(move _2) -> [false: bb4, otherwise: bb3]; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
}
|
||||
|
||||
- bb4: {
|
||||
+ bb3: {
|
||||
_0 = const (); // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
|
||||
- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:8:13: 8:18
|
||||
+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
|
||||
+ return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
|
||||
_0 = const (); // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
|
||||
- goto -> bb9; // scope 0 at $DIR/simplify_cfg.rs:10:13: 10:18
|
||||
+ StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
|
||||
+ return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
|
||||
}
|
||||
|
||||
- bb5: {
|
||||
+ bb4: {
|
||||
_1 = const (); // scope 0 at $DIR/simplify_cfg.rs:9:10: 9:10
|
||||
- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
_1 = const (); // scope 0 at $DIR/simplify_cfg.rs:11:10: 11:10
|
||||
- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
- unreachable; // scope 0 at $DIR/simplify_cfg.rs:7:18: 9:10
|
||||
- unreachable; // scope 0 at $DIR/simplify_cfg.rs:9:18: 11:10
|
||||
- }
|
||||
-
|
||||
- bb7: {
|
||||
- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:7:9: 9:10
|
||||
- goto -> bb8; // scope 0 at $DIR/simplify_cfg.rs:9:9: 11:10
|
||||
- }
|
||||
-
|
||||
- bb8: {
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
+ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:6:5: 10:6
|
||||
StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
|
||||
- goto -> bb1; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
+ goto -> bb0; // scope 0 at $DIR/simplify_cfg.rs:8:5: 12:6
|
||||
}
|
||||
|
||||
- bb9: {
|
||||
- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:9:9: 9:10
|
||||
- return; // scope 0 at $DIR/simplify_cfg.rs:11:2: 11:2
|
||||
- StorageDead(_2); // scope 0 at $DIR/simplify_cfg.rs:11:9: 11:10
|
||||
- return; // scope 0 at $DIR/simplify_cfg.rs:13:2: 13:2
|
||||
- }
|
||||
-
|
||||
- bb10 (cleanup): {
|
||||
+ bb5 (cleanup): {
|
||||
resume; // scope 0 at $DIR/simplify_cfg.rs:5:1: 11:2
|
||||
resume; // scope 0 at $DIR/simplify_cfg.rs:7:1: 13:2
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,6 @@
|
||||
// Test that the goto chain starting from bb0 is collapsed.
|
||||
// compile-flags: -Cpanic=abort
|
||||
// no-prefer-dynamic
|
||||
|
||||
// EMIT_MIR simplify_cfg.main.SimplifyCfg-initial.diff
|
||||
// EMIT_MIR simplify_cfg.main.SimplifyCfg-early-opt.diff
|
||||
|
@ -1,70 +1,69 @@
|
||||
1| |#![feature(unwind_attributes)]
|
||||
1| |#![feature(c_unwind)]
|
||||
2| |#![allow(unused_assignments)]
|
||||
3| |
|
||||
4| |#[unwind(aborts)]
|
||||
5| 12|fn might_abort(should_abort: bool) {
|
||||
6| 12| if should_abort {
|
||||
7| 0| println!("aborting...");
|
||||
8| 0| panic!("panics and aborts");
|
||||
9| 12| } else {
|
||||
10| 12| println!("Don't Panic");
|
||||
4| 12|extern "C" fn might_abort(should_abort: bool) {
|
||||
5| 12| if should_abort {
|
||||
6| 0| println!("aborting...");
|
||||
7| 0| panic!("panics and aborts");
|
||||
8| 12| } else {
|
||||
9| 12| println!("Don't Panic");
|
||||
10| 12| }
|
||||
11| 12|}
|
||||
12| 12|}
|
||||
13| |
|
||||
14| 1|fn main() -> Result<(), u8> {
|
||||
15| 1| let mut countdown = 10;
|
||||
16| 11| while countdown > 0 {
|
||||
17| 10| if countdown < 5 {
|
||||
18| 4| might_abort(false);
|
||||
19| 6| }
|
||||
20| | // See discussion (below the `Notes` section) on coverage results for the closing brace.
|
||||
21| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
|
||||
12| |
|
||||
13| 1|fn main() -> Result<(), u8> {
|
||||
14| 1| let mut countdown = 10;
|
||||
15| 11| while countdown > 0 {
|
||||
16| 10| if countdown < 5 {
|
||||
17| 4| might_abort(false);
|
||||
18| 6| }
|
||||
19| | // See discussion (below the `Notes` section) on coverage results for the closing brace.
|
||||
20| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
|
||||
^4 ^6
|
||||
22| | // For the following example, the closing brace is the last character on the line.
|
||||
23| | // This shows the character after the closing brace is highlighted, even if that next
|
||||
24| | // character is a newline.
|
||||
25| 10| if countdown < 5 { might_abort(false); }
|
||||
21| | // For the following example, the closing brace is the last character on the line.
|
||||
22| | // This shows the character after the closing brace is highlighted, even if that next
|
||||
23| | // character is a newline.
|
||||
24| 10| if countdown < 5 { might_abort(false); }
|
||||
^4 ^6
|
||||
26| 10| countdown -= 1;
|
||||
27| | }
|
||||
28| 1| Ok(())
|
||||
29| 1|}
|
||||
30| |
|
||||
31| |// Notes:
|
||||
32| |// 1. Compare this program and its coverage results to those of the similar tests
|
||||
33| |// `panic_unwind.rs` and `try_error_result.rs`.
|
||||
34| |// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`.
|
||||
35| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage
|
||||
36| |// results show where the program did and did not execute.
|
||||
37| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as
|
||||
38| |// intended"). Coverage results would show no executed coverage regions.
|
||||
39| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status
|
||||
40| |// (on Linux at least).
|
||||
41| |
|
||||
42| |/*
|
||||
43| |
|
||||
44| |Expect the following coverage results:
|
||||
45| |
|
||||
46| |```text
|
||||
47| | 16| 11| while countdown > 0 {
|
||||
48| | 17| 10| if countdown < 5 {
|
||||
49| | 18| 4| might_abort(false);
|
||||
50| | 19| 6| }
|
||||
51| |```
|
||||
52| |
|
||||
53| |This is actually correct.
|
||||
54| |
|
||||
55| |The condition `countdown < 5` executed 10 times (10 loop iterations).
|
||||
56| |
|
||||
57| |It evaluated to `true` 4 times, and executed the `might_abort()` call.
|
||||
58| |
|
||||
59| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit
|
||||
60| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s
|
||||
61| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the
|
||||
62| |non-true condition.
|
||||
63| |
|
||||
64| |As another example of why this is important, say the condition was `countdown < 50`, which is always
|
||||
65| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called.
|
||||
66| |The closing brace would have a count of `0`, highlighting the missed coverage.
|
||||
67| |*/
|
||||
25| 10| countdown -= 1;
|
||||
26| | }
|
||||
27| 1| Ok(())
|
||||
28| 1|}
|
||||
29| |
|
||||
30| |// Notes:
|
||||
31| |// 1. Compare this program and its coverage results to those of the similar tests
|
||||
32| |// `panic_unwind.rs` and `try_error_result.rs`.
|
||||
33| |// 2. This test confirms the coverage generated when a program includes `TerminatorKind::Abort`.
|
||||
34| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage
|
||||
35| |// results show where the program did and did not execute.
|
||||
36| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as
|
||||
37| |// intended"). Coverage results would show no executed coverage regions.
|
||||
38| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status
|
||||
39| |// (on Linux at least).
|
||||
40| |
|
||||
41| |/*
|
||||
42| |
|
||||
43| |Expect the following coverage results:
|
||||
44| |
|
||||
45| |```text
|
||||
46| | 16| 11| while countdown > 0 {
|
||||
47| | 17| 10| if countdown < 5 {
|
||||
48| | 18| 4| might_abort(false);
|
||||
49| | 19| 6| }
|
||||
50| |```
|
||||
51| |
|
||||
52| |This is actually correct.
|
||||
53| |
|
||||
54| |The condition `countdown < 5` executed 10 times (10 loop iterations).
|
||||
55| |
|
||||
56| |It evaluated to `true` 4 times, and executed the `might_abort()` call.
|
||||
57| |
|
||||
58| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit
|
||||
59| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s
|
||||
60| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the
|
||||
61| |non-true condition.
|
||||
62| |
|
||||
63| |As another example of why this is important, say the condition was `countdown < 50`, which is always
|
||||
64| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called.
|
||||
65| |The closing brace would have a count of `0`, highlighting the missed coverage.
|
||||
66| |*/
|
||||
|
||||
|
@ -29,12 +29,12 @@
|
||||
18| 2| println!("BOOM times {}!!!", self.strength);
|
||||
19| 2| }
|
||||
------------------
|
||||
| <generics::Firework<i32> as core::ops::drop::Drop>::drop:
|
||||
| <generics::Firework<f64> as core::ops::drop::Drop>::drop:
|
||||
| 17| 1| fn drop(&mut self) {
|
||||
| 18| 1| println!("BOOM times {}!!!", self.strength);
|
||||
| 19| 1| }
|
||||
------------------
|
||||
| <generics::Firework<f64> as core::ops::drop::Drop>::drop:
|
||||
| <generics::Firework<i32> as core::ops::drop::Drop>::drop:
|
||||
| 17| 1| fn drop(&mut self) {
|
||||
| 18| 1| println!("BOOM times {}!!!", self.strength);
|
||||
| 19| 1| }
|
||||
|
@ -19,12 +19,12 @@
|
||||
18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
|
||||
19| 2|}
|
||||
------------------
|
||||
| used_crate::used_only_from_bin_crate_generic_function::<&str>:
|
||||
| used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
|
||||
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
|
||||
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
|
||||
| 19| 1|}
|
||||
------------------
|
||||
| used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>:
|
||||
| used_crate::used_only_from_bin_crate_generic_function::<&str>:
|
||||
| 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) {
|
||||
| 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg);
|
||||
| 19| 1|}
|
||||
|
@ -1,8 +1,7 @@
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
#![allow(unused_assignments)]
|
||||
|
||||
#[unwind(aborts)]
|
||||
fn might_abort(should_abort: bool) {
|
||||
extern "C" fn might_abort(should_abort: bool) {
|
||||
if should_abort {
|
||||
println!("aborting...");
|
||||
panic!("panics and aborts");
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
// For linking libstdc++ on MinGW
|
||||
#![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
@ -18,22 +18,21 @@ impl<'a> Drop for DropCheck<'a> {
|
||||
|
||||
extern "C" {
|
||||
fn test_cxx_exception();
|
||||
}
|
||||
|
||||
#[unwind(allowed)]
|
||||
fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool);
|
||||
extern "C-unwind" {
|
||||
fn cxx_catch_callback(cb: extern "C-unwind" fn(), ok: *mut bool);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[unwind(allowed)]
|
||||
extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) {
|
||||
extern "C-unwind" fn rust_catch_callback(cb: extern "C-unwind" fn(), rust_ok: &mut bool) {
|
||||
let _drop = DropCheck(rust_ok);
|
||||
cb();
|
||||
unreachable!("should have unwound instead of returned");
|
||||
}
|
||||
|
||||
fn test_rust_panic() {
|
||||
#[unwind(allowed)]
|
||||
extern "C" fn callback() {
|
||||
extern "C-unwind" fn callback() {
|
||||
println!("throwing rust panic");
|
||||
panic!(1234i32);
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
#![feature(unwind_attributes, const_panic)]
|
||||
#![feature(c_unwind, const_panic, const_extern_fn)]
|
||||
|
||||
#[unwind(aborts)]
|
||||
const fn foo() {
|
||||
const extern "C" fn foo() {
|
||||
panic!() //~ ERROR evaluation of constant value failed
|
||||
}
|
||||
|
||||
const _: () = foo();
|
||||
// Ensure that the CTFE engine handles calls to `#[unwind(aborts)]` gracefully
|
||||
// Ensure that the CTFE engine handles calls to `extern "C"` aborting gracefully
|
||||
|
||||
fn main() {
|
||||
let _ = foo();
|
||||
|
@ -1,14 +1,14 @@
|
||||
error[E0080]: evaluation of constant value failed
|
||||
--> $DIR/unwind-abort.rs:5:5
|
||||
--> $DIR/unwind-abort.rs:4:5
|
||||
|
|
||||
LL | panic!()
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:5:5
|
||||
| the evaluated program panicked at 'explicit panic', $DIR/unwind-abort.rs:4:5
|
||||
| inside `foo` at $SRC_DIR/std/src/panic.rs:LL:COL
|
||||
...
|
||||
LL | const _: () = foo();
|
||||
| ----- inside `_` at $DIR/unwind-abort.rs:8:15
|
||||
| ----- inside `_` at $DIR/unwind-abort.rs:7:15
|
||||
|
|
||||
= note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
|
@ -1,10 +1,9 @@
|
||||
// check-pass
|
||||
|
||||
#![feature(unwind_attributes, const_panic)]
|
||||
#![feature(c_unwind, const_panic, const_extern_fn)]
|
||||
|
||||
// `#[unwind(aborts)]` is okay for a `const fn`. We don't unwind in const-eval anyways.
|
||||
#[unwind(aborts)]
|
||||
const fn foo() {
|
||||
// We don't unwind in const-eval anyways.
|
||||
const extern "C" fn foo() {
|
||||
panic!()
|
||||
}
|
||||
|
||||
|
@ -1,19 +0,0 @@
|
||||
// ignore-wasm32-bare compiled with panic=abort by default
|
||||
// compile-flags: -C no-prepopulate-passes -Cpasses=name-anon-globals
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
extern "C" {
|
||||
// CHECK: Function Attrs: nounwind
|
||||
// CHECK-NEXT: declare void @extern_fn
|
||||
fn extern_fn();
|
||||
// CHECK-NOT: Function Attrs: nounwind
|
||||
// CHECK: declare void @unwinding_extern_fn
|
||||
#[unwind(allowed)] //~ ERROR the `#[unwind]` attribute is an experimental feature
|
||||
fn unwinding_extern_fn();
|
||||
}
|
||||
|
||||
pub unsafe fn force_declare() {
|
||||
extern_fn();
|
||||
unwinding_extern_fn();
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
error[E0658]: the `#[unwind]` attribute is an experimental feature
|
||||
--> $DIR/feature-gate-unwind-attributes.rs:12:5
|
||||
|
|
||||
LL | #[unwind(allowed)]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #58760 <https://github.com/rust-lang/rust/issues/58760> for more information
|
||||
= help: add `#![feature(unwind_attributes)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
@ -1,9 +0,0 @@
|
||||
#![feature(unwind_attributes)]
|
||||
|
||||
#[unwind] //~ ERROR malformed `unwind` attribute
|
||||
extern "C" fn f1() {}
|
||||
|
||||
#[unwind = ""] //~ ERROR malformed `unwind` attribute
|
||||
extern "C" fn f2() {}
|
||||
|
||||
fn main() {}
|
@ -1,14 +0,0 @@
|
||||
error: malformed `unwind` attribute input
|
||||
--> $DIR/malformed-unwind-1.rs:3:1
|
||||
|
|
||||
LL | #[unwind]
|
||||
| ^^^^^^^^^ help: must be of the form: `#[unwind(allowed|aborts)]`
|
||||
|
||||
error: malformed `unwind` attribute input
|
||||
--> $DIR/malformed-unwind-1.rs:6:1
|
||||
|
|
||||
LL | #[unwind = ""]
|
||||
| ^^^^^^^^^^^^^^ help: must be of the form: `#[unwind(allowed|aborts)]`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -1,11 +0,0 @@
|
||||
#![feature(unwind_attributes)]
|
||||
|
||||
#[unwind(allowed, aborts)]
|
||||
//~^ ERROR malformed `unwind` attribute
|
||||
extern "C" fn f1() {}
|
||||
|
||||
#[unwind(unsupported)]
|
||||
//~^ ERROR malformed `unwind` attribute
|
||||
extern "C" fn f2() {}
|
||||
|
||||
fn main() {}
|
@ -1,29 +0,0 @@
|
||||
error[E0633]: malformed `unwind` attribute input
|
||||
--> $DIR/malformed-unwind-2.rs:3:1
|
||||
|
|
||||
LL | #[unwind(allowed, aborts)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid argument
|
||||
|
|
||||
help: the allowed arguments are `allowed` and `aborts`
|
||||
|
|
||||
LL | #[unwind(allowed)]
|
||||
|
|
||||
LL | #[unwind(aborts)]
|
||||
|
|
||||
|
||||
error[E0633]: malformed `unwind` attribute input
|
||||
--> $DIR/malformed-unwind-2.rs:7:1
|
||||
|
|
||||
LL | #[unwind(unsupported)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ invalid argument
|
||||
|
|
||||
help: the allowed arguments are `allowed` and `aborts`
|
||||
|
|
||||
LL | #[unwind(allowed)]
|
||||
|
|
||||
LL | #[unwind(aborts)]
|
||||
|
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0633`.
|
@ -1,7 +1,7 @@
|
||||
// run-pass
|
||||
|
||||
#![allow(unused_must_use)]
|
||||
#![feature(unwind_attributes)]
|
||||
#![feature(c_unwind)]
|
||||
#![feature(panic_always_abort)]
|
||||
// Since we mark some ABIs as "nounwind" to LLVM, we must make sure that
|
||||
// we never unwind through them.
|
||||
@ -9,23 +9,17 @@
|
||||
// ignore-emscripten no processes
|
||||
// ignore-sgx no processes
|
||||
|
||||
use std::{env, panic};
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::process::{exit, Command, Stdio};
|
||||
use std::sync::{Arc, Barrier};
|
||||
use std::thread;
|
||||
use std::{env, panic};
|
||||
|
||||
#[unwind(aborts)] // FIXME(#58794) should work even without the attribute
|
||||
extern "C" fn panic_in_ffi() {
|
||||
panic!("Test");
|
||||
}
|
||||
|
||||
#[unwind(aborts)]
|
||||
extern "Rust" fn panic_in_rust_abi() {
|
||||
panic!("TestRust");
|
||||
}
|
||||
|
||||
fn should_have_aborted() {
|
||||
io::stdout().write(b"This should never be printed.\n");
|
||||
let _ = io::stdout().flush();
|
||||
@ -37,18 +31,17 @@ fn bomb_out_but_not_abort(msg: &str) {
|
||||
}
|
||||
|
||||
fn test() {
|
||||
let _ = panic::catch_unwind(|| { panic_in_ffi(); });
|
||||
should_have_aborted();
|
||||
}
|
||||
|
||||
fn testrust() {
|
||||
let _ = panic::catch_unwind(|| { panic_in_rust_abi(); });
|
||||
let _ = panic::catch_unwind(|| {
|
||||
panic_in_ffi();
|
||||
});
|
||||
should_have_aborted();
|
||||
}
|
||||
|
||||
fn test_always_abort() {
|
||||
panic::always_abort();
|
||||
let _ = panic::catch_unwind(|| { panic!(); });
|
||||
let _ = panic::catch_unwind(|| {
|
||||
panic!();
|
||||
});
|
||||
should_have_aborted();
|
||||
}
|
||||
|
||||
@ -70,7 +63,6 @@ fn test_always_abort_thread() {
|
||||
fn main() {
|
||||
let tests: &[(_, fn())] = &[
|
||||
("test", test),
|
||||
("testrust", testrust),
|
||||
("test_always_abort", test_always_abort),
|
||||
("test_always_abort_thread", test_always_abort_thread),
|
||||
];
|
||||
@ -79,7 +71,9 @@ fn main() {
|
||||
if args.len() > 1 {
|
||||
// This is inside the self-executed command.
|
||||
for (a, f) in tests {
|
||||
if &args[1] == a { return f() }
|
||||
if &args[1] == a {
|
||||
return f();
|
||||
}
|
||||
}
|
||||
bomb_out_but_not_abort("bad test");
|
||||
}
|
||||
@ -88,7 +82,9 @@ fn main() {
|
||||
let mut p = Command::new(&args[0])
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.arg(arg).spawn().unwrap();
|
||||
.arg(arg)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let status = p.wait().unwrap();
|
||||
assert!(!status.success());
|
||||
// Any reasonable platform can distinguish a process which
|
||||
|
Loading…
Reference in New Issue
Block a user