Skip to content

Commit 17a07d7

Browse files
committed
Auto merge of #76570 - cratelyn:implement-rfc-2945-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 #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
2 parents 5fe790e + 05bf037 commit 17a07d7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+661
-70
lines changed

compiler/rustc_ast_lowering/src/item.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
319319
},
320320
ItemKind::ForeignMod(ref fm) => {
321321
if fm.abi.is_none() {
322-
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
322+
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
323323
}
324324
hir::ItemKind::ForeignMod {
325-
abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
325+
abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)),
326326
items: self
327327
.arena
328328
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
@@ -1334,8 +1334,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13341334
match ext {
13351335
Extern::None => abi::Abi::Rust,
13361336
Extern::Implicit => {
1337-
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
1338-
abi::Abi::C
1337+
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
1338+
abi::Abi::C { unwind: false }
13391339
}
13401340
Extern::Explicit(abi) => self.lower_abi(abi),
13411341
}

compiler/rustc_ast_passes/src/feature_gate.rs

+32
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,38 @@ impl<'a> PostExpansionVisitor<'a> {
164164
"C-cmse-nonsecure-call ABI is experimental and subject to change"
165165
);
166166
}
167+
"C-unwind" => {
168+
gate_feature_post!(
169+
&self,
170+
c_unwind,
171+
span,
172+
"C-unwind ABI is experimental and subject to change"
173+
);
174+
}
175+
"stdcall-unwind" => {
176+
gate_feature_post!(
177+
&self,
178+
c_unwind,
179+
span,
180+
"stdcall-unwind ABI is experimental and subject to change"
181+
);
182+
}
183+
"system-unwind" => {
184+
gate_feature_post!(
185+
&self,
186+
c_unwind,
187+
span,
188+
"system-unwind ABI is experimental and subject to change"
189+
);
190+
}
191+
"thiscall-unwind" => {
192+
gate_feature_post!(
193+
&self,
194+
c_unwind,
195+
span,
196+
"thiscall-unwind ABI is experimental and subject to change"
197+
);
198+
}
167199
abi => self
168200
.sess
169201
.parse_sess

compiler/rustc_codegen_cranelift/src/abi/mod.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -476,8 +476,11 @@ pub(crate) fn codegen_terminator_call<'tcx>(
476476

477477
// FIXME find a cleaner way to support varargs
478478
if fn_sig.c_variadic {
479-
if fn_sig.abi != Abi::C {
480-
fx.tcx.sess.span_fatal(span, &format!("Variadic call for non-C abi {:?}", fn_sig.abi));
479+
if !matches!(fn_sig.abi, Abi::C { .. }) {
480+
fx.tcx.sess.span_fatal(
481+
span,
482+
&format!("Variadic call for non-C abi {:?}", fn_sig.abi),
483+
);
481484
}
482485
let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap();
483486
let abi_params = call_args

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,9 @@ declare_features! (
641641
/// Allows associated types in inherent impls.
642642
(active, inherent_associated_types, "1.52.0", Some(8995), None),
643643

644+
/// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
645+
(active, c_unwind, "1.52.0", Some(74990), None),
646+
644647
// -------------------------------------------------------------------------
645648
// feature-group-end: actual feature gates
646649
// -------------------------------------------------------------------------

compiler/rustc_middle/src/ty/layout.rs

+39-16
Original file line numberDiff line numberDiff line change
@@ -2562,6 +2562,7 @@ fn fn_can_unwind(
25622562
panic_strategy: PanicStrategy,
25632563
codegen_fn_attr_flags: CodegenFnAttrFlags,
25642564
call_conv: Conv,
2565+
abi: SpecAbi,
25652566
) -> bool {
25662567
if panic_strategy != PanicStrategy::Unwind {
25672568
// In panic=abort mode we assume nothing can unwind anywhere, so
@@ -2586,17 +2587,34 @@ fn fn_can_unwind(
25862587
//
25872588
// 2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
25882589
//
2589-
// Foreign items (case 1) are assumed to not unwind; it is
2590-
// UB otherwise. (At least for now; see also
2591-
// rust-lang/rust#63909 and Rust RFC 2753.)
2592-
//
2593-
// Items defined in Rust with non-Rust ABIs (case 2) are also
2594-
// not supposed to unwind. Whether this should be enforced
2595-
// (versus stating it is UB) and *how* it would be enforced
2596-
// is currently under discussion; see rust-lang/rust#58794.
2597-
//
2598-
// In either case, we mark item as explicitly nounwind.
2599-
false
2590+
// In both of these cases, we should refer to the ABI to determine whether or not we
2591+
// should unwind. See Rust RFC 2945 for more information on this behavior, here:
2592+
// https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
2593+
use SpecAbi::*;
2594+
match abi {
2595+
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
2596+
unwind
2597+
}
2598+
Cdecl
2599+
| Fastcall
2600+
| Vectorcall
2601+
| Aapcs
2602+
| Win64
2603+
| SysV64
2604+
| PtxKernel
2605+
| Msp430Interrupt
2606+
| X86Interrupt
2607+
| AmdGpuKernel
2608+
| EfiApi
2609+
| AvrInterrupt
2610+
| AvrNonBlockingInterrupt
2611+
| CCmseNonSecureCall
2612+
| RustIntrinsic
2613+
| PlatformIntrinsic
2614+
| Unadjusted => false,
2615+
// In the `if` above, we checked for functions with the Rust calling convention.
2616+
Rust | RustCall => unreachable!(),
2617+
}
26002618
}
26012619
}
26022620
}
@@ -2654,14 +2672,14 @@ where
26542672
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
26552673

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

2660-
Stdcall => Conv::X86Stdcall,
2678+
Stdcall { .. } => Conv::X86Stdcall,
26612679
Fastcall => Conv::X86Fastcall,
26622680
Vectorcall => Conv::X86VectorCall,
2663-
Thiscall => Conv::X86ThisCall,
2664-
C => Conv::C,
2681+
Thiscall { .. } => Conv::X86ThisCall,
2682+
C { .. } => Conv::C,
26652683
Unadjusted => Conv::C,
26662684
Win64 => Conv::X86_64Win64,
26672685
SysV64 => Conv::X86_64SysV,
@@ -2823,7 +2841,12 @@ where
28232841
c_variadic: sig.c_variadic,
28242842
fixed_count: inputs.len(),
28252843
conv,
2826-
can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv),
2844+
can_unwind: fn_can_unwind(
2845+
cx.tcx().sess.panic_strategy(),
2846+
codegen_fn_attr_flags,
2847+
conv,
2848+
sig.abi,
2849+
),
28272850
};
28282851
fn_abi.adjust_for_abi(cx, sig.abi);
28292852
debug!("FnAbi::new_internal = {:?}", fn_abi);

compiler/rustc_mir/src/interpret/terminator.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
248248
};
249249
if normalize_abi(caller_abi) != normalize_abi(callee_abi) {
250250
throw_ub_format!(
251-
"calling a function with ABI {:?} using caller ABI {:?}",
252-
callee_abi,
253-
caller_abi
251+
"calling a function with ABI {} using caller ABI {}",
252+
callee_abi.name(),
253+
caller_abi.name()
254254
)
255255
}
256256
}

compiler/rustc_mir_build/src/build/mod.rs

+34-4
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ macro_rules! unpack {
548548
}};
549549
}
550550

551-
fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
551+
fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
552552
// Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
553553
let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
554554
let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
@@ -558,12 +558,42 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b
558558
return false;
559559
}
560560

561-
// This is a special case: some functions have a C abi but are meant to
562-
// unwind anyway. Don't stop them.
563561
match unwind_attr {
564-
None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
562+
// If an `#[unwind]` attribute was found, we should adhere to it.
565563
Some(UnwindAttr::Allowed) => false,
566564
Some(UnwindAttr::Aborts) => true,
565+
// If no attribute was found and the panic strategy is `unwind`, then we should examine
566+
// the function's ABI string to determine whether it should abort upon panic.
567+
None => {
568+
use Abi::*;
569+
match abi {
570+
// In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
571+
// permits unwinding. If so, we should not abort. Otherwise, we should.
572+
C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
573+
!unwind
574+
}
575+
// Rust and `rust-call` functions are allowed to unwind, and should not abort.
576+
Rust | RustCall => false,
577+
// Other ABI's should abort.
578+
Cdecl
579+
| Fastcall
580+
| Vectorcall
581+
| Aapcs
582+
| Win64
583+
| SysV64
584+
| PtxKernel
585+
| Msp430Interrupt
586+
| X86Interrupt
587+
| AmdGpuKernel
588+
| EfiApi
589+
| AvrInterrupt
590+
| AvrNonBlockingInterrupt
591+
| CCmseNonSecureCall
592+
| RustIntrinsic
593+
| PlatformIntrinsic
594+
| Unadjusted => true,
595+
}
596+
}
567597
}
568598
}
569599

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ symbols! {
330330
bridge,
331331
bswap,
332332
c_str,
333+
c_unwind,
333334
c_variadic,
334335
call,
335336
call_mut,

compiler/rustc_symbol_mangling/src/v0.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ impl Printer<'tcx> for SymbolMangler<'tcx> {
440440
}
441441
match sig.abi {
442442
Abi::Rust => {}
443-
Abi::C => cx.push("KC"),
443+
Abi::C { unwind: false } => cx.push("KC"),
444444
abi => {
445445
cx.push("K");
446446
let name = abi.name();

compiler/rustc_target/src/spec/abi.rs

+62-14
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,21 @@ mod tests;
88
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
99
#[derive(HashStable_Generic, Encodable, Decodable)]
1010
pub enum Abi {
11-
// N.B., this ordering MUST match the AbiDatas array below.
12-
// (This is ensured by the test indices_are_correct().)
13-
1411
// Multiplatform / generic ABIs
1512
//
1613
// These ABIs come first because every time we add a new ABI, we
1714
// have to re-bless all the hashing tests. These are used in many
1815
// places, so giving them stable values reduces test churn. The
1916
// specific values are meaningless.
20-
Rust = 0,
21-
C = 1,
17+
Rust,
18+
C { unwind: bool },
2219

2320
// Single platform ABIs
2421
Cdecl,
25-
Stdcall,
22+
Stdcall { unwind: bool },
2623
Fastcall,
2724
Vectorcall,
28-
Thiscall,
25+
Thiscall { unwind: bool },
2926
Aapcs,
3027
Win64,
3128
SysV64,
@@ -39,7 +36,7 @@ pub enum Abi {
3936
CCmseNonSecureCall,
4037

4138
// Multiplatform / generic ABIs
42-
System,
39+
System { unwind: bool },
4340
RustIntrinsic,
4441
RustCall,
4542
PlatformIntrinsic,
@@ -61,13 +58,16 @@ pub struct AbiData {
6158
const AbiDatas: &[AbiData] = &[
6259
// Cross-platform ABIs
6360
AbiData { abi: Abi::Rust, name: "Rust", generic: true },
64-
AbiData { abi: Abi::C, name: "C", generic: true },
61+
AbiData { abi: Abi::C { unwind: false }, name: "C", generic: true },
62+
AbiData { abi: Abi::C { unwind: true }, name: "C-unwind", generic: true },
6563
// Platform-specific ABIs
6664
AbiData { abi: Abi::Cdecl, name: "cdecl", generic: false },
67-
AbiData { abi: Abi::Stdcall, name: "stdcall", generic: false },
65+
AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall", generic: false },
66+
AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind", generic: false },
6867
AbiData { abi: Abi::Fastcall, name: "fastcall", generic: false },
6968
AbiData { abi: Abi::Vectorcall, name: "vectorcall", generic: false },
70-
AbiData { abi: Abi::Thiscall, name: "thiscall", generic: false },
69+
AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall", generic: false },
70+
AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind", generic: false },
7171
AbiData { abi: Abi::Aapcs, name: "aapcs", generic: false },
7272
AbiData { abi: Abi::Win64, name: "win64", generic: false },
7373
AbiData { abi: Abi::SysV64, name: "sysv64", generic: false },
@@ -84,7 +84,8 @@ const AbiDatas: &[AbiData] = &[
8484
},
8585
AbiData { abi: Abi::CCmseNonSecureCall, name: "C-cmse-nonsecure-call", generic: false },
8686
// Cross-platform ABIs
87-
AbiData { abi: Abi::System, name: "system", generic: true },
87+
AbiData { abi: Abi::System { unwind: false }, name: "system", generic: true },
88+
AbiData { abi: Abi::System { unwind: true }, name: "system-unwind", generic: true },
8889
AbiData { abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },
8990
AbiData { abi: Abi::RustCall, name: "rust-call", generic: true },
9091
AbiData { abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true },
@@ -103,7 +104,52 @@ pub fn all_names() -> Vec<&'static str> {
103104
impl Abi {
104105
#[inline]
105106
pub fn index(self) -> usize {
106-
self as usize
107+
// N.B., this ordering MUST match the AbiDatas array above.
108+
// (This is ensured by the test indices_are_correct().)
109+
use Abi::*;
110+
let i = match self {
111+
// Cross-platform ABIs
112+
Rust => 0,
113+
C { unwind: false } => 1,
114+
C { unwind: true } => 2,
115+
// Platform-specific ABIs
116+
Cdecl => 3,
117+
Stdcall { unwind: false } => 4,
118+
Stdcall { unwind: true } => 5,
119+
Fastcall => 6,
120+
Vectorcall => 7,
121+
Thiscall { unwind: false } => 8,
122+
Thiscall { unwind: true } => 9,
123+
Aapcs => 10,
124+
Win64 => 11,
125+
SysV64 => 12,
126+
PtxKernel => 13,
127+
Msp430Interrupt => 14,
128+
X86Interrupt => 15,
129+
AmdGpuKernel => 16,
130+
EfiApi => 17,
131+
AvrInterrupt => 18,
132+
AvrNonBlockingInterrupt => 19,
133+
CCmseNonSecureCall => 20,
134+
// Cross-platform ABIs
135+
System { unwind: false } => 21,
136+
System { unwind: true } => 22,
137+
RustIntrinsic => 23,
138+
RustCall => 24,
139+
PlatformIntrinsic => 25,
140+
Unadjusted => 26,
141+
};
142+
debug_assert!(
143+
AbiDatas
144+
.iter()
145+
.enumerate()
146+
.find(|(_, AbiData { abi, .. })| *abi == self)
147+
.map(|(index, _)| index)
148+
.expect("abi variant has associated data")
149+
== i,
150+
"Abi index did not match `AbiDatas` ordering"
151+
);
152+
i
107153
}
108154

109155
#[inline]
@@ -122,6 +168,8 @@ impl Abi {
122168

123169
impl fmt::Display for Abi {
124170
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125-
write!(f, "\"{}\"", self.name())
171+
match self {
172+
abi => write!(f, "\"{}\"", abi.name()),
173+
}
126174
}
127175
}

0 commit comments

Comments
 (0)