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:
parent
2939249f29
commit
1c07096a45
46 changed files with 431 additions and 478 deletions
|
@ -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,65 +2601,124 @@ 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:
|
||||
//
|
||||
// 1. A foreign item using a non-Rust ABI (like `extern "C" { fn foo(); }`), or
|
||||
//
|
||||
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
|
||||
//
|
||||
// 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
|
||||
use SpecAbi::*;
|
||||
match abi {
|
||||
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
|
||||
unwind
|
||||
}
|
||||
Cdecl
|
||||
| Fastcall
|
||||
| Vectorcall
|
||||
| Aapcs
|
||||
| Win64
|
||||
| SysV64
|
||||
| PtxKernel
|
||||
| Msp430Interrupt
|
||||
| X86Interrupt
|
||||
| AmdGpuKernel
|
||||
| EfiApi
|
||||
| AvrInterrupt
|
||||
| AvrNonBlockingInterrupt
|
||||
| CCmseNonSecureCall
|
||||
| Wasm
|
||||
| RustIntrinsic
|
||||
| PlatformIntrinsic
|
||||
| Unadjusted => false,
|
||||
// In the `if` above, we checked for functions with the Rust calling convention.
|
||||
Rust | RustCall => unreachable!(),
|
||||
}
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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
|
||||
| Vectorcall
|
||||
| Aapcs
|
||||
| Win64
|
||||
| SysV64
|
||||
| PtxKernel
|
||||
| Msp430Interrupt
|
||||
| X86Interrupt
|
||||
| AmdGpuKernel
|
||||
| EfiApi
|
||||
| AvrInterrupt
|
||||
| AvrNonBlockingInterrupt
|
||||
| CCmseNonSecureCall
|
||||
| Wasm
|
||||
| RustIntrinsic
|
||||
| PlatformIntrinsic
|
||||
| Unadjusted => false,
|
||||
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,
|
||||
]],
|
||||
);
|
||||
|
||||
|
|
124
compiler/rustc_mir/src/transform/abort_unwinding_calls.rs
Normal file
124
compiler/rustc_mir/src/transform/abort_unwinding_calls.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
use crate::transform::MirPass;
|
||||
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>) {
|
||||
// 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(body.source.def_id()).flags;
|
||||
let body_ty = tcx.type_of(body.source.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 (func, source_info) = match &block.terminator {
|
||||
Some(Terminator { kind: TerminatorKind::Call { func, .. }, source_info }) => {
|
||||
(func, source_info)
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
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!(source_info.span, "invalid callee of type {:?}", ty),
|
||||
};
|
||||
|
||||
let call_can_unwind = layout::fn_can_unwind(tcx, flags, sig.abi());
|
||||
|
||||
// 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 = match &mut body.basic_blocks_mut()[bb].terminator {
|
||||
Some(Terminator { kind: TerminatorKind::Call { cleanup, .. }, .. }) => cleanup,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
*cleanup = Some(abort_bb);
|
||||
}
|
||||
}
|
||||
|
||||
for id in cleanups_to_remove {
|
||||
let cleanup = match &mut body.basic_blocks_mut()[id].terminator {
|
||||
Some(Terminator { kind: TerminatorKind::Call { cleanup, .. }, .. }) => cleanup,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
*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,6 @@ 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,
|
||||
// AddMovesForPackedDrops needs to run after drop
|
||||
// elaboration.
|
||||
&add_moves_for_packed_drops::AddMovesForPackedDrops,
|
||||
|
@ -530,6 +528,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
|
||||
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
|
||||
let pre_codegen_cleanup: &[&dyn MirPass<'tcx>] = &[
|
||||
// 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,
|
||||
&add_call_guards::CriticalCallEdges,
|
||||
// Dump the end result for testing and debugging purposes.
|
||||
&dump_mir::Marker("PreCodegen"),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
// 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" {
|
||||
// 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
|
||||
extern "C-unwind" {
|
||||
// CHECK-NOT: nounwind
|
||||
// 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();
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
@ -18,11 +17,16 @@ fn main() -> Result<(), u8> {
|
|||
might_abort(false);
|
||||
}
|
||||
// See discussion (below the `Notes` section) on coverage results for the closing brace.
|
||||
if countdown < 5 { might_abort(false); } // Counts for different regions on one line.
|
||||
if countdown < 5 {
|
||||
might_abort(false);
|
||||
}
|
||||
// Counts for different regions on one line.
|
||||
// For the following example, the closing brace is the last character on the line.
|
||||
// This shows the character after the closing brace is highlighted, even if that next
|
||||
// character is a newline.
|
||||
if countdown < 5 { might_abort(false); }
|
||||
if countdown < 5 {
|
||||
might_abort(false);
|
||||
}
|
||||
countdown -= 1;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -56,7 +49,7 @@ fn test_always_abort_thread() {
|
|||
let barrier = Arc::new(Barrier::new(2));
|
||||
let thr = {
|
||||
let barrier = barrier.clone();
|
||||
thread::spawn(move ||{
|
||||
thread::spawn(move || {
|
||||
barrier.wait();
|
||||
panic!("in thread");
|
||||
})
|
||||
|
@ -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),
|
||||
];
|
||||
|
@ -78,17 +70,21 @@ fn main() {
|
|||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() > 1 {
|
||||
// This is inside the self-executed command.
|
||||
for (a,f) in tests {
|
||||
if &args[1] == a { return f() }
|
||||
for (a, f) in tests {
|
||||
if &args[1] == a {
|
||||
return f();
|
||||
}
|
||||
}
|
||||
bomb_out_but_not_abort("bad test");
|
||||
}
|
||||
|
||||
let execute_self_expecting_abort = |arg| {
|
||||
let mut p = Command::new(&args[0])
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.arg(arg).spawn().unwrap();
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::piped())
|
||||
.arg(arg)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
let status = p.wait().unwrap();
|
||||
assert!(!status.success());
|
||||
// Any reasonable platform can distinguish a process which
|
||||
|
@ -96,7 +92,7 @@ fn main() {
|
|||
assert_ne!(status.code(), Some(1));
|
||||
};
|
||||
|
||||
for (a,_f) in tests {
|
||||
for (a, _f) in tests {
|
||||
execute_self_expecting_abort(a);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue