Skip to content

Commit 6073b4c

Browse files
committed
add extern "custom" functions
1 parent f315e61 commit 6073b4c

File tree

34 files changed

+865
-9
lines changed

34 files changed

+865
-9
lines changed

compiler/rustc_abi/src/canon_abi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub enum CanonAbi {
2828
Rust,
2929
RustCold,
3030

31+
/// An ABI that rustc does not know how to call or define.
32+
Custom,
33+
3134
/// ABIs relevant to 32-bit Arm targets
3235
Arm(ArmCall),
3336
/// ABI relevant to GPUs: the entry point for a GPU kernel
@@ -79,6 +82,7 @@ impl fmt::Display for CanonAbi {
7982
X86Call::Vectorcall => ExternAbi::Vectorcall { unwind: false },
8083
X86Call::Win64 => ExternAbi::Win64 { unwind: false },
8184
},
85+
CanonAbi::Custom => ExternAbi::Custom,
8286
};
8387
erased_abi.as_str().fmt(f)
8488
}

compiler/rustc_abi/src/extern_abi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ pub enum ExternAbi {
2727
Cdecl {
2828
unwind: bool,
2929
},
30+
/// An ABI that rustc does not know how to call or define. Functions with this ABI can
31+
/// only be created using `#[naked]` functions or `extern "custom"` blocks, and can only
32+
/// be called from inline assembly.
33+
Custom,
3034
Stdcall {
3135
unwind: bool,
3236
},
@@ -114,6 +118,7 @@ abi_impls! {
114118
AvrNonBlockingInterrupt =><= "avr-non-blocking-interrupt",
115119
Cdecl { unwind: false } =><= "cdecl",
116120
Cdecl { unwind: true } =><= "cdecl-unwind",
121+
Custom =><= "custom",
117122
EfiApi =><= "efiapi",
118123
Fastcall { unwind: false } =><= "fastcall",
119124
Fastcall { unwind: true } =><= "fastcall-unwind",

compiler/rustc_ast/src/ast.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3521,6 +3521,37 @@ impl FnHeader {
35213521
|| matches!(constness, Const::Yes(_))
35223522
|| !matches!(ext, Extern::None)
35233523
}
3524+
3525+
pub fn span(&self) -> Option<Span> {
3526+
fn append(a: &mut Option<Span>, b: Span) {
3527+
*a = match a {
3528+
None => Some(b),
3529+
Some(x) => Some(x.to(b)),
3530+
}
3531+
}
3532+
3533+
let mut full_span = None;
3534+
3535+
match self.safety {
3536+
Safety::Unsafe(span) | Safety::Safe(span) => append(&mut full_span, span),
3537+
Safety::Default => {}
3538+
};
3539+
3540+
if let Some(coroutine_kind) = self.coroutine_kind {
3541+
append(&mut full_span, coroutine_kind.span());
3542+
}
3543+
3544+
if let Const::Yes(span) = self.constness {
3545+
append(&mut full_span, span);
3546+
}
3547+
3548+
match self.ext {
3549+
Extern::Implicit(span) | Extern::Explicit(_, span) => append(&mut full_span, span),
3550+
Extern::None => {}
3551+
}
3552+
3553+
full_span
3554+
}
35243555
}
35253556

35263557
impl Default for FnHeader {

compiler/rustc_ast_lowering/src/stability.rs

Lines changed: 3 additions & 0 deletions
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_ast_passes/messages.ftl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
ast_passes_abi_custom_invalid_signature =
2+
invalid signature for `extern "custom"` function
3+
.note = functions with the `"custom"` ABI cannot have any parameters or return type
4+
.suggestion = remove the parameters and return type
5+
6+
ast_passes_abi_custom_safe_foreign_function =
7+
foreign functions with the `"custom"` ABI cannot be safe
8+
.suggestion = remove the `safe` keyword from this definition
9+
10+
ast_passes_abi_custom_safe_function =
11+
functions with the `"custom"` ABI must be unsafe
12+
.suggestion = add the `unsafe` keyword to this definition
13+
114
ast_passes_assoc_const_without_body =
215
associated constant in `impl` without body
316
.suggestion = provide a definition for the constant

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
1919
use std::mem;
2020
use std::ops::{Deref, DerefMut};
21+
use std::str::FromStr;
2122

2223
use itertools::{Either, Itertools};
2324
use rustc_abi::ExternAbi;
@@ -81,6 +82,7 @@ struct AstValidator<'a> {
8182

8283
/// Used to ban explicit safety on foreign items when the extern block is not marked as unsafe.
8384
extern_mod_safety: Option<Safety>,
85+
extern_mod_abi: Option<ExternAbi>,
8486

8587
lint_node_id: NodeId,
8688

@@ -121,10 +123,17 @@ impl<'a> AstValidator<'a> {
121123
self.outer_trait_or_trait_impl = old;
122124
}
123125

124-
fn with_in_extern_mod(&mut self, extern_mod_safety: Safety, f: impl FnOnce(&mut Self)) {
125-
let old = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
126+
fn with_in_extern_mod(
127+
&mut self,
128+
extern_mod_safety: Safety,
129+
abi: Option<ExternAbi>,
130+
f: impl FnOnce(&mut Self),
131+
) {
132+
let old_safety = mem::replace(&mut self.extern_mod_safety, Some(extern_mod_safety));
133+
let old_abi = mem::replace(&mut self.extern_mod_abi, abi);
126134
f(self);
127-
self.extern_mod_safety = old;
135+
self.extern_mod_safety = old_safety;
136+
self.extern_mod_abi = old_abi;
128137
}
129138

130139
fn with_tilde_const(
@@ -370,6 +379,50 @@ impl<'a> AstValidator<'a> {
370379
}
371380
}
372381

382+
/// An `extern "custom"` function must be unsafe, and must not have any parameters or return
383+
/// type.
384+
fn check_custom_abi(&self, ctxt: FnCtxt, ident: &Ident, sig: &FnSig) {
385+
let dcx = self.dcx();
386+
387+
// An `extern "custom"` function must be unsafe.
388+
match sig.header.safety {
389+
Safety::Unsafe(_) => { /* all good */ }
390+
Safety::Safe(safe_span) => {
391+
let safe_span =
392+
self.sess.psess.source_map().span_until_non_whitespace(safe_span.to(sig.span));
393+
dcx.emit_err(errors::AbiCustomSafeForeignFunction { span: sig.span, safe_span });
394+
}
395+
Safety::Default => match ctxt {
396+
FnCtxt::Foreign => { /* all good */ }
397+
FnCtxt::Free | FnCtxt::Assoc(_) => {
398+
self.dcx().emit_err(errors::AbiCustomSafeFunction {
399+
span: sig.span,
400+
unsafe_span: sig.span.shrink_to_lo(),
401+
});
402+
}
403+
},
404+
}
405+
406+
// An `extern "custom"` function must not have any parameters or return type.
407+
let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect();
408+
if let FnRetTy::Ty(ref ret_ty) = sig.decl.output {
409+
spans.push(ret_ty.span);
410+
}
411+
412+
if !spans.is_empty() {
413+
let header_span = sig.header.span().unwrap_or(sig.span.shrink_to_lo());
414+
let suggestion_span = sig.span.with_lo(header_span.hi());
415+
let padding = if header_span.is_empty() { "" } else { " " };
416+
417+
self.dcx().emit_err(errors::AbiCustomInvalidSignature {
418+
spans,
419+
symbol: ident.name,
420+
suggestion_span,
421+
padding,
422+
});
423+
}
424+
}
425+
373426
/// This ensures that items can only be `unsafe` (or unmarked) outside of extern
374427
/// blocks.
375428
///
@@ -1005,7 +1058,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10051058
if abi.is_none() {
10061059
self.handle_missing_abi(*extern_span, item.id);
10071060
}
1008-
self.with_in_extern_mod(*safety, |this| {
1061+
1062+
let extern_abi = abi.and_then(|abi| ExternAbi::from_str(abi.symbol.as_str()).ok());
1063+
self.with_in_extern_mod(*safety, extern_abi, |this| {
10091064
visit::walk_item(this, item);
10101065
});
10111066
self.extern_mod_span = old_item;
@@ -1145,6 +1200,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
11451200
self.check_foreign_fn_bodyless(*ident, body.as_deref());
11461201
self.check_foreign_fn_headerless(sig.header);
11471202
self.check_foreign_item_ascii_only(*ident);
1203+
if self.extern_mod_abi == Some(ExternAbi::Custom) {
1204+
self.check_custom_abi(FnCtxt::Foreign, ident, sig);
1205+
}
11481206
}
11491207
ForeignItemKind::TyAlias(box TyAlias {
11501208
defaultness,
@@ -1352,6 +1410,13 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
13521410
self.check_item_safety(span, safety);
13531411
}
13541412

1413+
if let FnKind::Fn(ctxt, _, fun) = fk
1414+
&& let Extern::Explicit(str_lit, _) = fun.sig.header.ext
1415+
&& let Ok(ExternAbi::Custom) = ExternAbi::from_str(str_lit.symbol.as_str())
1416+
{
1417+
self.check_custom_abi(ctxt, &fun.ident, &fun.sig);
1418+
}
1419+
13551420
self.check_c_variadic_type(fk);
13561421

13571422
// Functions cannot both be `const async` or `const gen`
@@ -1703,6 +1768,7 @@ pub fn check_crate(
17031768
outer_impl_trait_span: None,
17041769
disallow_tilde_const: Some(TildeConstReason::Item),
17051770
extern_mod_safety: None,
1771+
extern_mod_abi: None,
17061772
lint_node_id: CRATE_NODE_ID,
17071773
is_sdylib_interface,
17081774
lint_buffer: lints,

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,3 +824,51 @@ pub(crate) struct MissingAbi {
824824
#[suggestion(code = "extern \"<abi>\"", applicability = "has-placeholders")]
825825
pub span: Span,
826826
}
827+
828+
#[derive(Diagnostic)]
829+
#[diag(ast_passes_abi_custom_safe_foreign_function)]
830+
pub(crate) struct AbiCustomSafeForeignFunction {
831+
#[primary_span]
832+
pub span: Span,
833+
834+
#[suggestion(
835+
ast_passes_suggestion,
836+
applicability = "maybe-incorrect",
837+
code = "",
838+
style = "verbose"
839+
)]
840+
pub safe_span: Span,
841+
}
842+
843+
#[derive(Diagnostic)]
844+
#[diag(ast_passes_abi_custom_safe_function)]
845+
pub(crate) struct AbiCustomSafeFunction {
846+
#[primary_span]
847+
pub span: Span,
848+
849+
#[suggestion(
850+
ast_passes_suggestion,
851+
applicability = "maybe-incorrect",
852+
code = "unsafe ",
853+
style = "verbose"
854+
)]
855+
pub unsafe_span: Span,
856+
}
857+
858+
#[derive(Diagnostic)]
859+
#[diag(ast_passes_abi_custom_invalid_signature)]
860+
#[note]
861+
pub(crate) struct AbiCustomInvalidSignature {
862+
#[primary_span]
863+
pub spans: Vec<Span>,
864+
865+
#[suggestion(
866+
ast_passes_suggestion,
867+
applicability = "maybe-incorrect",
868+
code = "{padding}fn {symbol}()",
869+
style = "verbose"
870+
)]
871+
pub suggestion_span: Span,
872+
pub symbol: Symbol,
873+
pub padding: &'static str,
874+
}

compiler/rustc_codegen_cranelift/src/abi/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ pub(crate) fn conv_to_call_conv(
6464
CanonAbi::GpuKernel => {
6565
unreachable!("tried to use {c:?} call conv which only exists on an unsupported target")
6666
}
67+
68+
// Functions with this calling convention can only be called from assembly, but it is
69+
// possible to declare an `extern "custom"` block, so the backend still needs a calling
70+
// convention for declaring foreign functions.
71+
CanonAbi::Custom => default_call_conv,
6772
}
6873
}
6974

compiler/rustc_codegen_gcc/src/abi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ pub fn conv_to_fn_attribute<'gcc>(conv: CanonAbi, arch: &str) -> Option<FnAttrib
273273
X86Call::SysV64 => FnAttribute::X86SysvAbi,
274274
X86Call::Win64 => FnAttribute::X86MsAbi,
275275
},
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+
CanonAbi::Custom => return None,
276280
};
277281
Some(attribute)
278282
}

compiler/rustc_codegen_llvm/src/abi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,10 @@ impl llvm::CallConv {
677677
X86Call::Vectorcall => llvm::X86_VectorCall,
678678
X86Call::Win64 => llvm::X86_64_Win64,
679679
},
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+
CanonAbi::Custom => llvm::CCallConv,
680684
}
681685
}
682686
}

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,8 @@ declare_features! (
353353
(unstable, abi_avr_interrupt, "1.45.0", Some(69664)),
354354
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
355355
(unstable, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391)),
356+
/// Allows `extern "custom" fn()`.
357+
(unstable, abi_custom, "CURRENT_RUSTC_VERSION", Some(140829)),
356358
/// Allows `extern "gpu-kernel" fn()`.
357359
(unstable, abi_gpu_kernel, "1.86.0", Some(135467)),
358360
/// Allows `extern "msp430-interrupt" fn()`.

compiler/rustc_hir_analysis/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
hir_analysis_abi_custom_clothed_function =
2+
functions with the `"custom"` ABI must be naked
3+
.suggestion = add the `#[unsafe(naked)]` attribute to this function
4+
15
hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`
26
.label = ambiguous associated {$assoc_kind} `{$assoc_ident}`
37

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_middle::ty::error::TypeErrorToStringExt;
2020
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
2121
use rustc_middle::ty::util::Discr;
2222
use rustc_middle::ty::{
23-
AdtDef, BottomUpFolder, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
23+
AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable,
2424
TypeVisitable, TypeVisitableExt, fold_regions,
2525
};
2626
use rustc_session::lint::builtin::UNINHABITED_STATIC;
@@ -57,6 +57,18 @@ pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ex
5757
}
5858
}
5959

60+
pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) {
61+
if fn_sig.abi == ExternAbi::Custom {
62+
// Function definitions that use `extern "custom"` must be naked functions.
63+
if !tcx.has_attr(def_id, sym::naked) {
64+
tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction {
65+
span: fn_sig_span,
66+
naked_span: tcx.def_span(def_id).shrink_to_lo(),
67+
});
68+
}
69+
}
70+
}
71+
6072
fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
6173
let def = tcx.adt_def(def_id);
6274
let span = tcx.def_span(def_id);

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub mod wfcheck;
7272

7373
use std::num::NonZero;
7474

75-
pub use check::{check_abi, check_abi_fn_ptr};
75+
pub use check::{check_abi, check_abi_fn_ptr, check_custom_abi};
7676
use rustc_abi::{ExternAbi, VariantIdx};
7777
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
7878
use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};

compiler/rustc_hir_analysis/src/errors.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,3 +1707,17 @@ pub(crate) struct SelfInTypeAlias {
17071707
#[label]
17081708
pub span: Span,
17091709
}
1710+
1711+
#[derive(Diagnostic)]
1712+
#[diag(hir_analysis_abi_custom_clothed_function)]
1713+
pub(crate) struct AbiCustomClothedFunction {
1714+
#[primary_span]
1715+
pub span: Span,
1716+
#[suggestion(
1717+
hir_analysis_suggestion,
1718+
applicability = "maybe-incorrect",
1719+
code = "#[unsafe(naked)]\n",
1720+
style = "short"
1721+
)]
1722+
pub naked_span: Span,
1723+
}

compiler/rustc_hir_typeck/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
hir_typeck_abi_custom_call =
2+
functions with the `"custom"` ABI cannot be called
3+
.note = an `extern "custom"` function can only be called from within inline assembly
4+
15
hir_typeck_add_missing_parentheses_in_range = you must surround the range in parentheses to call its `{$func_name}` function
26
37
hir_typeck_add_return_type_add = try adding a return type

0 commit comments

Comments
 (0)