Skip to content

Commit df45c57

Browse files
author
katelyn a. martin
committed
rustc_target: add "unwind" payloads to Abi
### Overview This commit begins the implementation work for RFC 2945. For more information, see the rendered RFC [1] and tracking issue [2]. 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]. ### Feature Gate and Unstable Book 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. ### Further Work To Be Done This commit does not proceed to implement the new unwinding ABIs, and is intentionally scoped specifically to *defining* the ABIs and their feature flag. ### One Note on Test Churn This will lead to some test churn, in re-blessing hash tests, as the deleted comment in `src/librustc_target/spec/abi.rs` mentioned, because we can no longer guarantee the ordering of the `Abi` variants. While this is a downside, this decision was made bearing in mind that RFC 2945 states the following, in the "Other `unwind` Strings" section [3]: > More unwind variants of existing ABI strings may be introduced, > with the same semantics, without an additional RFC. Adding a new variant for each of these cases, rather than specifying a payload for a given ABI, would quickly become untenable, and make working with the `Abi` enum prone to mistakes. This approach encodes the unwinding information *into* a given ABI, to account for the future possibility of other `-unwind` ABI strings. ### Ignore Directives `ignore-*` directives are used in two of our `*-unwind` ABI test cases. Specifically, the `stdcall-unwind` and `thiscall-unwind` test cases ignore architectures that do not support `stdcall` and `thiscall`, respectively. These directives are cribbed from `src/test/ui/c-variadic/variadic-ffi-1.rs` for `stdcall`, and `src/test/ui/extern/extern-thiscall.rs` for `thiscall`. This would otherwise fail on some targets, see: rust-lang-ci@fcf697f ### Footnotes [1]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md [2]: #74990 [3]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md#other-unwind-abi-strings
1 parent 3a5d45f commit df45c57

File tree

29 files changed

+277
-52
lines changed

29 files changed

+277
-52
lines changed

compiler/rustc_ast_lowering/src/item.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -322,10 +322,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
322322
},
323323
ItemKind::ForeignMod(ref fm) => {
324324
if fm.abi.is_none() {
325-
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
325+
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
326326
}
327327
hir::ItemKind::ForeignMod {
328-
abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
328+
abi: fm.abi.map_or(abi::Abi::C { unwind: false }, |abi| self.lower_abi(abi)),
329329
items: self
330330
.arena
331331
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
@@ -1322,8 +1322,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
13221322
match ext {
13231323
Extern::None => abi::Abi::Rust,
13241324
Extern::Implicit => {
1325-
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
1326-
abi::Abi::C
1325+
self.maybe_lint_missing_abi(span, id, abi::Abi::C { unwind: false });
1326+
abi::Abi::C { unwind: false }
13271327
}
13281328
Extern::Explicit(abi) => self.lower_abi(abi),
13291329
}

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
@@ -644,6 +644,9 @@ declare_features! (
644644
/// Allows associated types in inherent impls.
645645
(active, inherent_associated_types, "1.52.0", Some(8995), None),
646646

647+
/// Allows `extern "C-unwind" fn` to enable unwinding across ABI boundaries.
648+
(active, c_unwind, "1.52.0", Some(74990), None),
649+
647650
// -------------------------------------------------------------------------
648651
// feature-group-end: actual feature gates
649652
// -------------------------------------------------------------------------

compiler/rustc_middle/src/ty/layout.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2654,14 +2654,14 @@ where
26542654
RustIntrinsic | PlatformIntrinsic | Rust | RustCall => Conv::Rust,
26552655

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

2660-
Stdcall => Conv::X86Stdcall,
2660+
Stdcall { .. } => Conv::X86Stdcall,
26612661
Fastcall => Conv::X86Fastcall,
26622662
Vectorcall => Conv::X86VectorCall,
2663-
Thiscall => Conv::X86ThisCall,
2664-
C => Conv::C,
2663+
Thiscall { .. } => Conv::X86ThisCall,
2664+
C { .. } => Conv::C,
26652665
Unadjusted => Conv::C,
26662666
Win64 => Conv::X86_64Win64,
26672667
SysV64 => Conv::X86_64SysV,

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

+51-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,41 @@ 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+
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+
}
107142
}
108143

109144
#[inline]
@@ -122,6 +157,8 @@ impl Abi {
122157

123158
impl fmt::Display for Abi {
124159
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125-
write!(f, "\"{}\"", self.name())
160+
match self {
161+
abi => write!(f, "\"{}\"", abi.name()),
162+
}
126163
}
127164
}

compiler/rustc_target/src/spec/arm_base.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,14 @@ use crate::spec::abi::Abi;
22

33
// All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm
44
pub fn unsupported_abis() -> Vec<Abi> {
5-
vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Thiscall, Abi::Win64, Abi::SysV64]
5+
vec![
6+
Abi::Stdcall { unwind: false },
7+
Abi::Stdcall { unwind: true },
8+
Abi::Fastcall,
9+
Abi::Vectorcall,
10+
Abi::Thiscall { unwind: false },
11+
Abi::Thiscall { unwind: true },
12+
Abi::Win64,
13+
Abi::SysV64,
14+
]
615
}

compiler/rustc_target/src/spec/mipsel_unknown_none.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ pub fn target() -> Target {
2323
panic_strategy: PanicStrategy::Abort,
2424
relocation_model: RelocModel::Static,
2525
unsupported_abis: vec![
26-
Abi::Stdcall,
26+
Abi::Stdcall { unwind: false },
27+
Abi::Stdcall { unwind: true },
2728
Abi::Fastcall,
2829
Abi::Vectorcall,
29-
Abi::Thiscall,
30+
Abi::Thiscall { unwind: false },
31+
Abi::Thiscall { unwind: true },
3032
Abi::Win64,
3133
Abi::SysV64,
3234
],

compiler/rustc_target/src/spec/mod.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -1283,24 +1283,31 @@ impl Target {
12831283
/// Given a function ABI, turn it into the correct ABI for this target.
12841284
pub fn adjust_abi(&self, abi: Abi) -> Abi {
12851285
match abi {
1286-
Abi::System => {
1286+
Abi::System { unwind } => {
12871287
if self.is_like_windows && self.arch == "x86" {
1288-
Abi::Stdcall
1288+
Abi::Stdcall { unwind }
12891289
} else {
1290-
Abi::C
1290+
Abi::C { unwind }
12911291
}
12921292
}
12931293
// These ABI kinds are ignored on non-x86 Windows targets.
12941294
// See https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
12951295
// and the individual pages for __stdcall et al.
1296-
Abi::Stdcall | Abi::Fastcall | Abi::Vectorcall | Abi::Thiscall => {
1297-
if self.is_like_windows && self.arch != "x86" { Abi::C } else { abi }
1296+
Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => {
1297+
if self.is_like_windows && self.arch != "x86" { Abi::C { unwind } } else { abi }
1298+
}
1299+
Abi::Fastcall | Abi::Vectorcall => {
1300+
if self.is_like_windows && self.arch != "x86" {
1301+
Abi::C { unwind: false }
1302+
} else {
1303+
abi
1304+
}
12981305
}
12991306
Abi::EfiApi => {
13001307
if self.arch == "x86_64" {
13011308
Abi::Win64
13021309
} else {
1303-
Abi::C
1310+
Abi::C { unwind: false }
13041311
}
13051312
}
13061313
abi => abi,

compiler/rustc_target/src/spec/nvptx64_nvidia_cuda.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ pub fn target() -> Target {
4949
// create the tests for this.
5050
unsupported_abis: vec![
5151
Abi::Cdecl,
52-
Abi::Stdcall,
52+
Abi::Stdcall { unwind: false },
53+
Abi::Stdcall { unwind: true },
5354
Abi::Fastcall,
5455
Abi::Vectorcall,
55-
Abi::Thiscall,
56+
Abi::Thiscall { unwind: false },
57+
Abi::Thiscall { unwind: true },
5658
Abi::Aapcs,
5759
Abi::Win64,
5860
Abi::SysV64,

compiler/rustc_target/src/spec/riscv_base.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ use crate::spec::abi::Abi;
55
pub fn unsupported_abis() -> Vec<Abi> {
66
vec![
77
Abi::Cdecl,
8-
Abi::Stdcall,
8+
Abi::Stdcall { unwind: false },
9+
Abi::Stdcall { unwind: true },
910
Abi::Fastcall,
1011
Abi::Vectorcall,
11-
Abi::Thiscall,
12+
Abi::Thiscall { unwind: false },
13+
Abi::Thiscall { unwind: true },
1214
Abi::Aapcs,
1315
Abi::Win64,
1416
Abi::SysV64,

compiler/rustc_typeck/src/collect.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2666,7 +2666,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
26662666
} else if tcx.sess.check_name(attr, sym::used) {
26672667
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
26682668
} else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) {
2669-
if tcx.fn_sig(id).abi() != abi::Abi::C {
2669+
if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) {
26702670
struct_span_err!(
26712671
tcx.sess,
26722672
attr.span,

compiler/rustc_typeck/src/lib.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,19 @@ use astconv::AstConv;
118118
use bounds::Bounds;
119119

120120
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
121-
if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
122-
let mut err = struct_span_err!(
123-
tcx.sess,
124-
span,
125-
E0045,
126-
"C-variadic function must have C or cdecl calling convention"
127-
);
128-
err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
121+
match (decl.c_variadic, abi) {
122+
// The function has the correct calling convention, or isn't a "C-variadic" function.
123+
(false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {}
124+
// The function is a "C-variadic" function with an incorrect calling convention.
125+
(true, _) => {
126+
let mut err = struct_span_err!(
127+
tcx.sess,
128+
span,
129+
E0045,
130+
"C-variadic function must have C or cdecl calling convention"
131+
);
132+
err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
133+
}
129134
}
130135
}
131136

0 commit comments

Comments
 (0)