Skip to content

Commit 93971b9

Browse files
committed
add extern "custom" functions
1 parent 3ef8e64 commit 93971b9

File tree

28 files changed

+657
-0
lines changed

28 files changed

+657
-0
lines changed

compiler/rustc_abi/src/extern_abi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ pub enum ExternAbi {
7070
RustCold,
7171
RiscvInterruptM,
7272
RiscvInterruptS,
73+
/// An ABI that rustc does not know how to call or define. Functions with this ABI can
74+
/// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
75+
/// be called from inline assembly.
76+
Custom,
7377
}
7478

7579
macro_rules! abi_impls {
@@ -141,6 +145,7 @@ abi_impls! {
141145
Win64 { unwind: false } =><= "win64",
142146
Win64 { unwind: true } =><= "win64-unwind",
143147
X86Interrupt =><= "x86-interrupt",
148+
Custom =><= "custom",
144149
}
145150
}
146151

compiler/rustc_ast_lowering/src/stability.rs

+3
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,8 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
134134
feature: sym::cmse_nonsecure_entry,
135135
explain: GateReason::Experimental,
136136
}),
137+
ExternAbi::Custom => {
138+
Err(UnstableAbi { abi, feature: sym::abi_custom, explain: GateReason::Experimental })
139+
}
137140
}
138141
}

compiler/rustc_codegen_cranelift/src/abi/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: Call
6868
Conv::Msp430Intr | Conv::GpuKernel | Conv::AvrInterrupt | Conv::AvrNonBlockingInterrupt => {
6969
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target");
7070
}
71+
72+
// Functions with this calling convention can only be called from assembly, but it is
73+
// possible to declare an `extern "custom"` block, so the backend still needs a calling
74+
// convention for declaring foreign functions.
75+
Conv::Custom => default_call_conv,
7176
}
7277
}
7378

compiler/rustc_codegen_gcc/src/abi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,11 @@ pub fn conv_to_fn_attribute<'gcc>(conv: Conv, arch: &str) -> Option<FnAttribute<
272272
Conv::X86VectorCall => return None,
273273
Conv::X86_64SysV => FnAttribute::SysvAbi,
274274
Conv::X86_64Win64 => FnAttribute::MsAbi,
275+
276+
// Functions with this calling convention can only be called from assembly, but it is
277+
// possible to declare an `extern "custom"` block, so the backend still needs a calling
278+
// convention for declaring foreign functions.
279+
Conv::Custom => return None,
275280
};
276281
Some(attribute)
277282
}

compiler/rustc_codegen_llvm/src/abi.rs

+5
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,11 @@ impl llvm::CallConv {
676676
Conv::X86VectorCall => llvm::X86_VectorCall,
677677
Conv::X86_64SysV => llvm::X86_64_SysV,
678678
Conv::X86_64Win64 => llvm::X86_64_Win64,
679+
680+
// Functions with this calling convention can only be called from assembly, but it is
681+
// possible to declare an `extern "custom"` block, so the backend still needs a calling
682+
// convention for declaring foreign functions.
683+
Conv::Custom => llvm::CCallConv,
679684
}
680685
}
681686
}

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ declare_features! (
354354
(unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
355355
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
356356
(unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
357+
/// Allows `extern "custom" fn()`.
358+
(unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140566)),
357359
/// Allows `extern "gpu-kernel" fn()`.
358360
(unstable, abi_gpu_kernel, "1.86.0", Some(135467)),
359361
/// Allows `extern "msp430-interrupt" fn()`.

compiler/rustc_interface/src/passes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,7 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
960960
tcx.ensure_ok().check_mod_loops(module);
961961
tcx.ensure_ok().check_mod_attrs(module);
962962
tcx.ensure_ok().check_mod_naked_functions(module);
963+
tcx.ensure_ok().check_mod_custom_abi(module);
963964
tcx.ensure_ok().check_mod_unstable_api_usage(module);
964965
});
965966
},

compiler/rustc_middle/src/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,10 @@ rustc_queries! {
11171117
desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) }
11181118
}
11191119

1120+
query check_mod_custom_abi(key: LocalModDefId) {
1121+
desc { |tcx| "checking use of `extern \"custom\"` in {}", describe_as_module(key, tcx) }
1122+
}
1123+
11201124
query check_mod_privacy(key: LocalModDefId) {
11211125
desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) }
11221126
}

compiler/rustc_middle/src/ty/layout.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,7 @@ pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: ExternAbi)
12641264
| RiscvInterruptS
12651265
| CCmseNonSecureCall
12661266
| CCmseNonSecureEntry
1267+
| Custom
12671268
| Unadjusted => false,
12681269
Rust | RustCall | RustCold => tcx.sess.panic_strategy() == PanicStrategy::Unwind,
12691270
}

compiler/rustc_passes/messages.ftl

+16
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,22 @@
44
-passes_see_issue =
55
see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information
66
7+
passes_abi_custom_call =
8+
functions with the `"custom"` ABI cannot be called
9+
.note = an `extern "custom"` function can only be called from within inline assembly
10+
11+
passes_abi_custom_clothed_function =
12+
functions with the `"custom"` ABI must be naked
13+
.suggestion = add the `#[unsafe(naked)]` attribute to this function
14+
15+
passes_abi_custom_safe_foreign_function =
16+
foreign functions with the `"custom"` ABI cannot be safe
17+
.suggestion = remove the `safe` keyword from this definition
18+
19+
passes_abi_custom_safe_function =
20+
functions with the `"custom"` ABI must be unsafe
21+
.suggestion = add the `unsafe` keyword this function definition
22+
723
passes_abi_invalid_attribute =
824
`#[rustc_abi]` can only be applied to function items, type aliases, and associated functions
925
passes_abi_ne =
+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
use rustc_abi::ExternAbi;
2+
use rustc_hir::def::DefKind;
3+
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
4+
use rustc_hir::intravisit::Visitor;
5+
use rustc_hir::{self as hir, ExprKind, FnSig};
6+
use rustc_middle::hir::nested_filter::OnlyBodies;
7+
use rustc_middle::query::Providers;
8+
use rustc_middle::ty::{self, TyCtxt};
9+
use rustc_span::{BytePos, sym};
10+
11+
use crate::errors::{
12+
AbiCustomCall, AbiCustomClothedFunction, AbiCustomSafeForeignFunction, AbiCustomSafeFunction,
13+
};
14+
15+
pub(crate) fn provide(providers: &mut Providers) {
16+
*providers = Providers { check_mod_custom_abi, ..*providers };
17+
}
18+
19+
fn check_mod_custom_abi(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
20+
if !tcx.features().abi_custom() {
21+
return;
22+
}
23+
24+
let items = tcx.hir_module_items(module_def_id);
25+
for def_id in items.definitions() {
26+
let def_kind = tcx.def_kind(def_id);
27+
28+
// An `extern "custom"` function cannot be marked as `safe`.
29+
if let DefKind::ForeignMod = def_kind
30+
&& let hir::Node::Item(item) = tcx.hir_node_by_def_id(def_id)
31+
&& let hir::ItemKind::ForeignMod { abi: ExternAbi::Custom, items } = item.kind
32+
{
33+
for item in items {
34+
let hir_id = item.id.hir_id();
35+
if let hir::Node::ForeignItem(foreign_item) = tcx.hir_node(hir_id)
36+
&& let hir::ForeignItemKind::Fn(sig, _, _) = foreign_item.kind
37+
&& sig.header.is_safe()
38+
{
39+
let len = "safe ".len() as u32;
40+
let safe_span = sig.span.shrink_to_lo().with_hi(sig.span.lo() + BytePos(len));
41+
tcx.dcx().emit_err(AbiCustomSafeForeignFunction { span: sig.span, safe_span });
42+
}
43+
}
44+
}
45+
46+
// An `extern "custom"` function cannot be a `const fn`, because `naked_asm!` cannot be
47+
// evaluated at compile time, and `extern` blocks cannot declare `const fn` functions.
48+
// Therefore, to find all calls to `extern "custom"` functions, it suffices to traverse
49+
// all function bodies (i.e. we can skip `const` and `static` initializers).
50+
if !matches!(def_kind, DefKind::Fn | DefKind::AssocFn) {
51+
continue;
52+
}
53+
54+
let body = match tcx.hir_node_by_def_id(def_id) {
55+
hir::Node::Item(hir::Item {
56+
kind: hir::ItemKind::Fn { sig, body: body_id, .. },
57+
..
58+
})
59+
| hir::Node::ImplItem(hir::ImplItem {
60+
kind: hir::ImplItemKind::Fn(sig, body_id),
61+
..
62+
}) => {
63+
check_signature(tcx, def_id, sig, true);
64+
tcx.hir_body(*body_id)
65+
}
66+
hir::Node::TraitItem(hir::TraitItem {
67+
kind: hir::TraitItemKind::Fn(sig, trait_fn),
68+
..
69+
}) => match trait_fn {
70+
hir::TraitFn::Required(_) => {
71+
check_signature(tcx, def_id, sig, false);
72+
continue;
73+
}
74+
hir::TraitFn::Provided(body_id) => {
75+
check_signature(tcx, def_id, sig, true);
76+
tcx.hir_body(*body_id)
77+
}
78+
},
79+
_ => continue,
80+
};
81+
82+
let mut visitor = CheckCustomAbi { tcx };
83+
visitor.visit_body(body);
84+
}
85+
}
86+
87+
fn check_signature<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, sig: &FnSig<'tcx>, has_body: bool) {
88+
if sig.header.abi == ExternAbi::Custom {
89+
// Function definitions that use `extern "custom"` must be naked functions.
90+
if has_body && !tcx.has_attr(def_id, sym::naked) {
91+
tcx.dcx().emit_err(AbiCustomClothedFunction {
92+
span: sig.span,
93+
naked_span: sig.span.shrink_to_lo(),
94+
});
95+
}
96+
97+
// Function definitions that use `extern "custom"` must unsafe.
98+
if sig.header.is_safe() {
99+
tcx.dcx().emit_err(AbiCustomSafeFunction {
100+
span: sig.span,
101+
unsafe_span: sig.span.shrink_to_lo(),
102+
});
103+
}
104+
}
105+
}
106+
107+
struct CheckCustomAbi<'tcx> {
108+
tcx: TyCtxt<'tcx>,
109+
}
110+
111+
impl<'tcx> Visitor<'tcx> for CheckCustomAbi<'tcx> {
112+
type NestedFilter = OnlyBodies;
113+
114+
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
115+
self.tcx
116+
}
117+
118+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
119+
let opt_span_and_abi = match expr.kind {
120+
ExprKind::Call(fun, _) => {
121+
let fun_ty = self.tcx.typeck(fun.hir_id.owner.def_id).node_type(fun.hir_id);
122+
123+
match *fun_ty.kind() {
124+
ty::FnDef(def_id, _) => {
125+
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
126+
Some((expr.span, sig.abi))
127+
}
128+
ty::FnPtr(_, header) => Some((expr.span, header.abi)),
129+
_ => None,
130+
}
131+
}
132+
133+
ExprKind::MethodCall(_, receiver, _, span) => {
134+
let opt_def_id = self
135+
.tcx
136+
.typeck(receiver.hir_id.owner.def_id)
137+
.type_dependent_def_id(expr.hir_id);
138+
139+
opt_def_id.map(|def_id| {
140+
let sig = self.tcx.fn_sig(def_id).skip_binder().skip_binder();
141+
(span, sig.abi)
142+
})
143+
}
144+
_ => None,
145+
};
146+
147+
if let Some((span, ExternAbi::Custom)) = opt_span_and_abi {
148+
self.tcx.dcx().emit_err(AbiCustomCall { span });
149+
}
150+
151+
hir::intravisit::walk_expr(self, expr);
152+
}
153+
}

compiler/rustc_passes/src/errors.rs

+51
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,57 @@ pub(crate) struct AbiInvalidAttribute {
10441044
pub span: Span,
10451045
}
10461046

1047+
#[derive(Diagnostic)]
1048+
#[diag(passes_abi_custom_safe_foreign_function)]
1049+
pub(crate) struct AbiCustomSafeForeignFunction {
1050+
#[primary_span]
1051+
pub span: Span,
1052+
1053+
#[suggestion(
1054+
passes_suggestion,
1055+
applicability = "maybe-incorrect",
1056+
code = "",
1057+
style = "verbose"
1058+
)]
1059+
pub safe_span: Span,
1060+
}
1061+
1062+
#[derive(Diagnostic)]
1063+
#[diag(passes_abi_custom_safe_function)]
1064+
pub(crate) struct AbiCustomSafeFunction {
1065+
#[primary_span]
1066+
pub span: Span,
1067+
1068+
#[suggestion(
1069+
passes_suggestion,
1070+
applicability = "maybe-incorrect",
1071+
code = "unsafe ",
1072+
style = "verbose"
1073+
)]
1074+
pub unsafe_span: Span,
1075+
}
1076+
1077+
#[derive(Diagnostic)]
1078+
#[diag(passes_abi_custom_clothed_function)]
1079+
pub(crate) struct AbiCustomClothedFunction {
1080+
#[primary_span]
1081+
pub span: Span,
1082+
#[suggestion(
1083+
passes_suggestion,
1084+
applicability = "maybe-incorrect",
1085+
code = "#[unsafe(naked)]\n",
1086+
style = "short"
1087+
)]
1088+
pub naked_span: Span,
1089+
}
1090+
1091+
#[derive(Diagnostic)]
1092+
#[diag(passes_abi_custom_call)]
1093+
pub(crate) struct AbiCustomCall {
1094+
#[primary_span]
1095+
pub span: Span,
1096+
}
1097+
10471098
#[derive(Diagnostic)]
10481099
#[diag(passes_unrecognized_argument)]
10491100
pub(crate) struct UnrecognizedArgument {

compiler/rustc_passes/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use rustc_middle::query::Providers;
2020
pub mod abi_test;
2121
mod check_attr;
2222
mod check_export;
23+
mod custom_abi;
2324
pub mod dead;
2425
mod debugger_visualizer;
2526
mod diagnostic_items;
@@ -51,6 +52,7 @@ pub fn provide(providers: &mut Providers) {
5152
lib_features::provide(providers);
5253
loops::provide(providers);
5354
naked_functions::provide(providers);
55+
custom_abi::provide(providers);
5456
liveness::provide(providers);
5557
reachable::provide(providers);
5658
stability::provide(providers);

compiler/rustc_smir/src/rustc_internal/internal.rs

+1
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,7 @@ impl RustcInternal for Abi {
496496
Abi::RustCold => rustc_abi::ExternAbi::RustCold,
497497
Abi::RiscvInterruptM => rustc_abi::ExternAbi::RiscvInterruptM,
498498
Abi::RiscvInterruptS => rustc_abi::ExternAbi::RiscvInterruptS,
499+
Abi::Custom => rustc_abi::ExternAbi::Custom,
499500
}
500501
}
501502
}

compiler/rustc_smir/src/rustc_smir/convert/abi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ impl<'tcx> Stable<'tcx> for callconv::Conv {
117117
Conv::AvrInterrupt => CallConvention::AvrInterrupt,
118118
Conv::AvrNonBlockingInterrupt => CallConvention::AvrNonBlockingInterrupt,
119119
Conv::RiscvInterrupt { .. } => CallConvention::RiscvInterrupt,
120+
Conv::Custom => CallConvention::Custom,
120121
}
121122
}
122123
}

compiler/rustc_smir/src/rustc_smir/convert/ty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::ExternAbi {
879879
ExternAbi::RustCold => Abi::RustCold,
880880
ExternAbi::RiscvInterruptM => Abi::RiscvInterruptM,
881881
ExternAbi::RiscvInterruptS => Abi::RiscvInterruptS,
882+
ExternAbi::Custom => Abi::Custom,
882883
}
883884
}
884885
}

compiler/rustc_smir/src/stable_mir/abi.rs

+2
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,8 @@ pub enum CallConvention {
430430
PreserveMost,
431431
PreserveAll,
432432

433+
Custom,
434+
433435
// Target-specific calling conventions.
434436
ArmAapcs,
435437
CCmseNonSecureCall,

compiler/rustc_smir/src/stable_mir/ty.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,7 @@ pub enum Abi {
10981098
RustCold,
10991099
RiscvInterruptM,
11001100
RiscvInterruptS,
1101+
Custom,
11011102
}
11021103

11031104
/// A binder represents a possibly generic type and its bound vars.

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ symbols! {
413413
abi_amdgpu_kernel,
414414
abi_avr_interrupt,
415415
abi_c_cmse_nonsecure_call,
416+
abi_custom,
416417
abi_efiapi,
417418
abi_gpu_kernel,
418419
abi_msp430_interrupt,

0 commit comments

Comments
 (0)