Skip to content

Commit

Permalink
Rollup merge of rust-lang#76570 - katie-martin-fastly:implement-rfc-2…
Browse files Browse the repository at this point in the history
…945-c-unwind-abi, r=Amanieu

Implement RFC 2945: "C-unwind" ABI

## Implement RFC 2945: "C-unwind" ABI

This branch implements [RFC 2945]. The tracking issue for this RFC is rust-lang#74990.

The feature gate for the issue is `#![feature(c_unwind)]`.

This RFC was created as part of the ffi-unwind project group tracked at rust-lang/lang-team#19.

### Changes

Further details will be provided in commit messages, but a high-level overview
of the changes follows:

* A boolean `unwind` payload is added to the `C`, `System`, `Stdcall`,
and `Thiscall` variants, marking whether unwinding across FFI boundaries is
acceptable. The cases where each of these variants' `unwind` member is true
correspond with the `C-unwind`, `system-unwind`, `stdcall-unwind`, and
`thiscall-unwind` ABI strings introduced in RFC 2945 [3].

* This commit adds a `c_unwind` feature gate for the new ABI strings.
Tests for this feature gate are included in `src/test/ui/c-unwind/`, which
ensure that this feature gate works correctly for each of the new ABIs.
A new language features entry in the unstable book is added as well.

* We adjust the `rustc_middle::ty::layout::fn_can_unwind` function,
used to compute whether or not a `FnAbi` object represents a function that
should be able to unwind when `panic=unwind` is in use.

* Changes are also made to
`rustc_mir_build::build::should_abort_on_panic` so that the function ABI is
used to determind whether it should abort, assuming that the `panic=unwind`
strategy is being used, and no explicit unwind attribute was provided.

[RFC 2945]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
  • Loading branch information
Dylan-DPC authored Jan 28, 2021
2 parents 643a79a + 5d75cde commit 7c182eb
Show file tree
Hide file tree
Showing 48 changed files with 672 additions and 80 deletions.
8 changes: 4 additions & 4 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
ItemKind::Mod(ref m) => hir::ItemKind::Mod(self.lower_mod(m)),
ItemKind::ForeignMod(ref fm) => {
if fm.abi.is_none() {
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
}
hir::ItemKind::ForeignMod {
abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)),
items: self
.arena
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
Expand Down Expand Up @@ -1315,8 +1315,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
match ext {
Extern::None => abi::Abi::Rust,
Extern::Implicit => {
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
abi::Abi::C
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
abi::Abi::C { unwind: false }
}
Extern::Explicit(abi) => self.lower_abi(abi),
}
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,39 @@ impl<'a> PostExpansionVisitor<'a> {
"efiapi ABI is experimental and subject to change"
);
}
// FFI Unwinding ABI's
"C-unwind" => {
gate_feature_post!(
&self,
c_unwind,
span,
"C-unwind ABI is experimental and subject to change"
);
}
"stdcall-unwind" => {
gate_feature_post!(
&self,
c_unwind,
span,
"stdcall-unwind ABI is experimental and subject to change"
);
}
"system-unwind" => {
gate_feature_post!(
&self,
c_unwind,
span,
"system-unwind ABI is experimental and subject to change"
);
}
"thiscall-unwind" => {
gate_feature_post!(
&self,
c_unwind,
span,
"thiscall-unwind ABI is experimental and subject to change"
);
}
abi => self
.sess
.parse_sess
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_cranelift/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn clif_sig_from_fn_sig<'tcx>(
requires_caller_location: bool,
) -> Signature {
let abi = match sig.abi {
Abi::System => Abi::C,
Abi::System { unwind: false } => Abi::C { unwind: false },
abi => abi,
};
let (call_conv, inputs, output): (CallConv, Vec<Ty<'tcx>>, Ty<'tcx>) = match abi {
Expand All @@ -110,7 +110,7 @@ fn clif_sig_from_fn_sig<'tcx>(
sig.inputs().to_vec(),
sig.output(),
),
Abi::C | Abi::Unadjusted => (
Abi::C { unwind: false } | Abi::Unadjusted => (
CallConv::triple_default(triple),
sig.inputs().to_vec(),
sig.output(),
Expand All @@ -126,7 +126,7 @@ fn clif_sig_from_fn_sig<'tcx>(
inputs.extend(extra_args.types());
(CallConv::triple_default(triple), inputs, sig.output())
}
Abi::System => unreachable!(),
Abi::System { .. } => unreachable!(),
Abi::RustIntrinsic => (
CallConv::triple_default(triple),
sig.inputs().to_vec(),
Expand Down Expand Up @@ -664,7 +664,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(

// FIXME find a cleaner way to support varargs
if fn_sig.c_variadic {
if fn_sig.abi != Abi::C {
if !matches!(fn_sig.abi, Abi::C { .. }) {
fx.tcx.sess.span_fatal(
span,
&format!("Variadic call for non-C abi {:?}", fn_sig.abi),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,10 @@ declare_features! (

/// Allows using `pointer` and `reference` in intra-doc links
(active, intra_doc_pointers, "1.51.0", Some(80896), None),

/// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
(active, c_unwind, "1.51.0", Some(74990), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
54 changes: 38 additions & 16 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2546,6 +2546,7 @@ fn fn_can_unwind(
panic_strategy: PanicStrategy,
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
Expand All @@ -2570,17 +2571,33 @@ fn fn_can_unwind(
//
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
//
// Foreign items (case 1) are assumed to not unwind; it is
// UB otherwise. (At least for now; see also
// rust-lang/rust#63909 and Rust RFC 2753.)
//
// Items defined in Rust with non-Rust ABIs (case 2) are also
// not supposed to unwind. Whether this should be enforced
// (versus stating it is UB) and *how* it would be enforced
// is currently under discussion; see rust-lang/rust#58794.
//
// In either case, we mark item as explicitly nounwind.
false
// 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
| RustIntrinsic
| PlatformIntrinsic
| Unadjusted => false,
// In the `if` above, we checked for functions with the Rust calling convention.
Rust | RustCall => unreachable!(),
}
}
}
}
Expand Down Expand Up @@ -2638,14 +2655,14 @@ where
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,

// It's the ABI's job to select this, not ours.
System => bug!("system abi should be selected elsewhere"),
System { .. } => bug!("system abi should be selected elsewhere"),
EfiApi => bug!("eficall abi should be selected elsewhere"),

Stdcall => Conv::X86Stdcall,
Stdcall { .. } => Conv::X86Stdcall,
Fastcall => Conv::X86Fastcall,
Vectorcall => Conv::X86VectorCall,
Thiscall => Conv::X86ThisCall,
C => Conv::C,
Thiscall { .. } => Conv::X86ThisCall,
C { .. } => Conv::C,
Unadjusted => Conv::C,
Win64 => Conv::X86_64Win64,
SysV64 => Conv::X86_64SysV,
Expand Down Expand Up @@ -2806,7 +2823,12 @@ 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),
can_unwind: fn_can_unwind(
cx.tcx().sess.panic_strategy(),
codegen_fn_attr_flags,
conv,
sig.abi,
),
};
fn_abi.adjust_for_abi(cx, sig.abi);
debug!("FnAbi::new_internal = {:?}", fn_abi);
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_mir/src/interpret/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
};
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
throw_ub_format!(
"calling a function with ABI {:?} using caller ABI {:?}",
callee_abi,
caller_abi
"calling a function with ABI {} using caller ABI {}",
callee_abi.name(),
caller_abi.name()
)
}
}
Expand Down
37 changes: 33 additions & 4 deletions compiler/rustc_mir_build/src/build/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ macro_rules! unpack {
}};
}

fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
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);
Expand All @@ -556,12 +556,41 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b
return false;
}

// This is a special case: some functions have a C abi but are meant to
// unwind anyway. Don't stop them.
match unwind_attr {
None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
// 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 => {
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
| RustIntrinsic
| PlatformIntrinsic
| Unadjusted => true,
}
}
}
}

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ symbols! {
bridge,
bswap,
c_str,
c_unwind,
c_variadic,
call,
call_mut,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_symbol_mangling/src/v0.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
}
match sig.abi {
Abi::Rust => {}
Abi::C => cx.push("KC"),
Abi::C { unwind: false } => cx.push("KC"),
abi => {
cx.push("K");
let name = abi.name();
Expand Down
Loading

0 comments on commit 7c182eb

Please sign in to comment.