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:
Alex Crichton 2021-06-08 11:23:58 -07:00
parent 2939249f29
commit 1c07096a45
46 changed files with 431 additions and 478 deletions

View file

@ -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]`

View file

@ -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

View file

@ -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),

View file

@ -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 \

View file

@ -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
// -------------------------------------------------------------------------

View file

@ -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;

View file

@ -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>,
},

View file

@ -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);

View file

@ -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) => {

View file

@ -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,
]],
);

View 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);
}
}

View file

@ -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);

View file

@ -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"),

View file

@ -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();
}
}
}

View file

@ -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()
}

View file

@ -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];
}

View file

@ -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
}

View file

@ -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)]

View file

@ -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)

View file

@ -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) -> !;
}

View file

@ -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)]

View file

@ -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>,

View file

@ -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! {

View file

@ -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;
}

View file

@ -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);

View file

@ -10,6 +10,7 @@
// ignore-riscv64 FIXME
#![crate_type = "lib"]
#![feature(c_unwind)]
extern "C" {
fn bar();

View file

@ -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))
}

View file

@ -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{{.*}} }

View file

@ -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() {}

View file

@ -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

View 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() {}

View 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();
}

View file

@ -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() {}

View file

@ -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();
}

View file

@ -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(())

View file

@ -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);
}

View file

@ -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();

View file

@ -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)

View file

@ -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!()
}

View file

@ -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();
}

View file

@ -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`.

View file

@ -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() {}

View file

@ -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

View file

@ -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() {}

View file

@ -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`.

View file

@ -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);
}
}