From 1c07096a45a15de64216f12ec726333870e372b1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 8 Jun 2021 11:23:58 -0700 Subject: [PATCH] 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. --- compiler/rustc_attr/src/builtin.rs | 44 ----- .../src/error_codes/E0633.md | 4 +- compiler/rustc_feature/src/active.rs | 5 - compiler/rustc_feature/src/builtin_attrs.rs | 4 - compiler/rustc_feature/src/removed.rs | 5 + .../src/middle/codegen_fn_attrs.rs | 10 +- compiler/rustc_middle/src/mir/terminator.rs | 2 +- compiler/rustc_middle/src/ty/layout.rs | 181 +++++++++++------- .../rustc_mir/src/interpret/terminator.rs | 9 +- compiler/rustc_mir/src/shim.rs | 4 +- .../src/transform/abort_unwinding_calls.rs | 124 ++++++++++++ compiler/rustc_mir/src/transform/generator.rs | 5 - compiler/rustc_mir/src/transform/mod.rs | 8 +- .../src/transform/no_landing_pads.rs | 28 --- compiler/rustc_mir_build/src/build/mod.rs | 61 +----- compiler/rustc_mir_build/src/build/scope.rs | 19 +- compiler/rustc_typeck/src/collect.rs | 13 +- library/core/src/lib.rs | 1 - library/panic_unwind/src/lib.rs | 5 +- library/panic_unwind/src/seh.rs | 12 +- library/std/src/lib.rs | 2 +- library/std/src/panicking.rs | 5 +- library/unwind/src/lib.rs | 2 +- library/unwind/src/libunwind.rs | 13 +- src/test/codegen/c-variadic.rs | 7 +- src/test/codegen/catch-unwind.rs | 1 + src/test/codegen/try-panic-abort.rs | 20 -- .../unwind-abis/c-unwind-abi-panic-abort.rs | 18 +- .../nounwind-on-stable-panic-abort.rs | 16 ++ .../nounwind-on-stable-panic-unwind.rs | 17 ++ src/test/codegen/unwind-abis/nounwind.rs | 17 ++ src/test/codegen/unwind-and-panic-abort.rs | 16 ++ src/test/codegen/unwind-extern-exports.rs | 10 +- src/test/codegen/unwind-extern-imports.rs | 36 +--- src/test/run-make-fulldeps/coverage/abort.rs | 14 +- .../foreign-exceptions/foo.rs | 13 +- src/test/ui/consts/const-eval/unwind-abort.rs | 7 +- .../ui/consts/const-eval/unwind-abort.stderr | 6 +- src/test/ui/consts/unwind-abort.rs | 7 +- .../feature-gate-unwind-attributes.rs | 19 -- .../feature-gate-unwind-attributes.stderr | 12 -- src/test/ui/malformed/malformed-unwind-1.rs | 9 - .../ui/malformed/malformed-unwind-1.stderr | 14 -- src/test/ui/malformed/malformed-unwind-2.rs | 11 -- .../ui/malformed/malformed-unwind-2.stderr | 29 --- src/test/ui/panics/abort-on-panic.rs | 44 ++--- 46 files changed, 431 insertions(+), 478 deletions(-) create mode 100644 compiler/rustc_mir/src/transform/abort_unwinding_calls.rs delete mode 100644 compiler/rustc_mir/src/transform/no_landing_pads.rs delete mode 100644 src/test/codegen/try-panic-abort.rs create mode 100644 src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs create mode 100644 src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs create mode 100644 src/test/codegen/unwind-abis/nounwind.rs create mode 100644 src/test/codegen/unwind-and-panic-abort.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-unwind-attributes.rs delete mode 100644 src/test/ui/feature-gates/feature-gate-unwind-attributes.stderr delete mode 100644 src/test/ui/malformed/malformed-unwind-1.rs delete mode 100644 src/test/ui/malformed/malformed-unwind-1.stderr delete mode 100644 src/test/ui/malformed/malformed-unwind-2.rs delete mode 100644 src/test/ui/malformed/malformed-unwind-2.stderr diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index b7b053cd5ddb7..a50fc698850c6 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -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 { - 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]` diff --git a/compiler/rustc_error_codes/src/error_codes/E0633.md b/compiler/rustc_error_codes/src/error_codes/E0633.md index 4d1f0c47829c9..5b6c15c82eb63 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0633.md +++ b/compiler/rustc_error_codes/src/error_codes/E0633.md @@ -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 diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index db8d01e31b74d..128b51c206162 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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), diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 36d035cdfd3ae..49cca3fbc3985 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -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 \ diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index f63c207a540c2..46b36f2b7040f 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -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 // ------------------------------------------------------------------------- diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs index 93e7aeaffce37..b2705c7693914 100644 --- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs +++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs @@ -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; diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index c8db4aeb449b8..e78b6fd092de2 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -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, }, diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs index 95ea38d32b695..2e4395cfca8c1 100644 --- a/compiler/rustc_middle/src/ty/layout.rs +++ b/compiler/rustc_middle/src/ty/layout.rs @@ -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, 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); diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs index f369480d959fe..5265af7d7db79 100644 --- a/compiler/rustc_mir/src/interpret/terminator.rs +++ b/compiler/rustc_mir/src/interpret/terminator.rs @@ -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) => { diff --git a/compiler/rustc_mir/src/shim.rs b/compiler/rustc_mir/src/shim.rs index 796d024771d7f..82c5e85dcecc0 100644 --- a/compiler/rustc_mir/src/shim.rs +++ b/compiler/rustc_mir/src/shim.rs @@ -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, ]], ); diff --git a/compiler/rustc_mir/src/transform/abort_unwinding_calls.rs b/compiler/rustc_mir/src/transform/abort_unwinding_calls.rs new file mode 100644 index 0000000000000..1a84ce45be323 --- /dev/null +++ b/compiler/rustc_mir/src/transform/abort_unwinding_calls.rs @@ -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); + } +} diff --git a/compiler/rustc_mir/src/transform/generator.rs b/compiler/rustc_mir/src/transform/generator.rs index 3560b4b1e8645..ce4540b124fc1 100644 --- a/compiler/rustc_mir/src/transform/generator.rs +++ b/compiler/rustc_mir/src/transform/generator.rs @@ -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); diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs index e58a7d903082e..6cc95bab49208 100644 --- a/compiler/rustc_mir/src/transform/mod.rs +++ b/compiler/rustc_mir/src/transform/mod.rs @@ -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"), diff --git a/compiler/rustc_mir/src/transform/no_landing_pads.rs b/compiler/rustc_mir/src/transform/no_landing_pads.rs deleted file mode 100644 index 5479f0cc5861d..0000000000000 --- a/compiler/rustc_mir/src/transform/no_landing_pads.rs +++ /dev/null @@ -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(); - } - } -} diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 60cfd73b19a6e..0d623806eb7e1 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -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() } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 3de894bd37056..496db58758cdc 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -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, ) { let mut blocks = IndexVec::from_elem(None, &drops.drops); blocks[ROOT_NODE] = *resume_block; drops.build_mir::(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]; } diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index ce74d6fec9eae..06cb33b9ebf4b 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -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 } diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 25970722c2dad..1ab7227933bbb 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -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)] diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 3622bc8294b08..14b454da4f46f 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -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) diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs index 754dbb77af6ca..9f1eb411ff660 100644 --- a/library/panic_unwind/src/seh.rs +++ b/library/panic_unwind/src/seh.rs @@ -233,15 +233,14 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { // support capturing exceptions with std::exception_ptr, which we can't support // because Box 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) -> u32 { ptr!(exception_copy) as u32, ); - extern "system" { - #[unwind(allowed)] + extern "system-unwind" { fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 21a1ee137efad..9dd99e44e809b 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -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)] diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index 5f43393e58583..7de70091becf4 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -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>, diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index eaeec72fbb55b..9e52802450682 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -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! { diff --git a/library/unwind/src/libunwind.rs b/library/unwind/src/libunwind.rs index faf554d285a9a..196be74decba4 100644 --- a/library/unwind/src/libunwind.rs +++ b/library/unwind/src/libunwind.rs @@ -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; } diff --git a/src/test/codegen/c-variadic.rs b/src/test/codegen/c-variadic.rs index e038ed704513d..668d023af96a5 100644 --- a/src/test/codegen/c-variadic.rs +++ b/src/test/codegen/c-variadic.rs @@ -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); diff --git a/src/test/codegen/catch-unwind.rs b/src/test/codegen/catch-unwind.rs index 7ff9c0d15e003..b89c590add0d7 100644 --- a/src/test/codegen/catch-unwind.rs +++ b/src/test/codegen/catch-unwind.rs @@ -10,6 +10,7 @@ // ignore-riscv64 FIXME #![crate_type = "lib"] +#![feature(c_unwind)] extern "C" { fn bar(); diff --git a/src/test/codegen/try-panic-abort.rs b/src/test/codegen/try-panic-abort.rs deleted file mode 100644 index 166d2bb99426d..0000000000000 --- a/src/test/codegen/try-panic-abort.rs +++ /dev/null @@ -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)) -} diff --git a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs index afd65ff6741a6..398937a04c923 100644 --- a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs +++ b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs @@ -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{{.*}} } diff --git a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs new file mode 100644 index 0000000000000..9a4b3d3b48480 --- /dev/null +++ b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-abort.rs @@ -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() {} diff --git a/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs new file mode 100644 index 0000000000000..2783c83d3efe0 --- /dev/null +++ b/src/test/codegen/unwind-abis/nounwind-on-stable-panic-unwind.rs @@ -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 diff --git a/src/test/codegen/unwind-abis/nounwind.rs b/src/test/codegen/unwind-abis/nounwind.rs new file mode 100644 index 0000000000000..cfc140361f623 --- /dev/null +++ b/src/test/codegen/unwind-abis/nounwind.rs @@ -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() {} diff --git a/src/test/codegen/unwind-and-panic-abort.rs b/src/test/codegen/unwind-and-panic-abort.rs new file mode 100644 index 0000000000000..05d97f3256aca --- /dev/null +++ b/src/test/codegen/unwind-and-panic-abort.rs @@ -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(); +} diff --git a/src/test/codegen/unwind-extern-exports.rs b/src/test/codegen/unwind-extern-exports.rs index 487de20671a2c..c939235fb5006 100644 --- a/src/test/codegen/unwind-extern-exports.rs +++ b/src/test/codegen/unwind-extern-exports.rs @@ -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() {} diff --git a/src/test/codegen/unwind-extern-imports.rs b/src/test/codegen/unwind-extern-imports.rs index e28397eb1392d..e33e3e80521c1 100644 --- a/src/test/codegen/unwind-extern-imports.rs +++ b/src/test/codegen/unwind-extern-imports.rs @@ -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(); } diff --git a/src/test/run-make-fulldeps/coverage/abort.rs b/src/test/run-make-fulldeps/coverage/abort.rs index 52c6ff333e4ea..3d78752eeb9de 100644 --- a/src/test/run-make-fulldeps/coverage/abort.rs +++ b/src/test/run-make-fulldeps/coverage/abort.rs @@ -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(()) diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs index b5c8c1962a8ec..c279cf7e8bf10 100644 --- a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs @@ -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); } diff --git a/src/test/ui/consts/const-eval/unwind-abort.rs b/src/test/ui/consts/const-eval/unwind-abort.rs index 766a0c49be68a..89b5135fd37e3 100644 --- a/src/test/ui/consts/const-eval/unwind-abort.rs +++ b/src/test/ui/consts/const-eval/unwind-abort.rs @@ -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(); diff --git a/src/test/ui/consts/const-eval/unwind-abort.stderr b/src/test/ui/consts/const-eval/unwind-abort.stderr index e3b871ee529be..78ebd36abd86b 100644 --- a/src/test/ui/consts/const-eval/unwind-abort.stderr +++ b/src/test/ui/consts/const-eval/unwind-abort.stderr @@ -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) diff --git a/src/test/ui/consts/unwind-abort.rs b/src/test/ui/consts/unwind-abort.rs index f9011f908a75e..d9d0b91011d9a 100644 --- a/src/test/ui/consts/unwind-abort.rs +++ b/src/test/ui/consts/unwind-abort.rs @@ -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!() } diff --git a/src/test/ui/feature-gates/feature-gate-unwind-attributes.rs b/src/test/ui/feature-gates/feature-gate-unwind-attributes.rs deleted file mode 100644 index cd348ded4d7ce..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-unwind-attributes.rs +++ /dev/null @@ -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(); -} diff --git a/src/test/ui/feature-gates/feature-gate-unwind-attributes.stderr b/src/test/ui/feature-gates/feature-gate-unwind-attributes.stderr deleted file mode 100644 index 8e7895555c7a3..0000000000000 --- a/src/test/ui/feature-gates/feature-gate-unwind-attributes.stderr +++ /dev/null @@ -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 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`. diff --git a/src/test/ui/malformed/malformed-unwind-1.rs b/src/test/ui/malformed/malformed-unwind-1.rs deleted file mode 100644 index 009695b177f96..0000000000000 --- a/src/test/ui/malformed/malformed-unwind-1.rs +++ /dev/null @@ -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() {} diff --git a/src/test/ui/malformed/malformed-unwind-1.stderr b/src/test/ui/malformed/malformed-unwind-1.stderr deleted file mode 100644 index 0a553e8a245f6..0000000000000 --- a/src/test/ui/malformed/malformed-unwind-1.stderr +++ /dev/null @@ -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 - diff --git a/src/test/ui/malformed/malformed-unwind-2.rs b/src/test/ui/malformed/malformed-unwind-2.rs deleted file mode 100644 index 9aafc7ca9b851..0000000000000 --- a/src/test/ui/malformed/malformed-unwind-2.rs +++ /dev/null @@ -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() {} diff --git a/src/test/ui/malformed/malformed-unwind-2.stderr b/src/test/ui/malformed/malformed-unwind-2.stderr deleted file mode 100644 index 28512bf9ef10f..0000000000000 --- a/src/test/ui/malformed/malformed-unwind-2.stderr +++ /dev/null @@ -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`. diff --git a/src/test/ui/panics/abort-on-panic.rs b/src/test/ui/panics/abort-on-panic.rs index 2aea607e95489..1f6ad64c071f5 100644 --- a/src/test/ui/panics/abort-on-panic.rs +++ b/src/test/ui/panics/abort-on-panic.rs @@ -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 = 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); } }